You've already forked The-API-Book
mirror of
https://github.com/twirl/The-API-Book.git
synced 2025-08-10 21:51:42 +02:00
use only let
This commit is contained in:
@@ -230,10 +230,10 @@ The condition “coffee might be prepared” would look like `has_beans && has_c
|
||||
This advice contradicts the previous one, ironically. When developing APIs you frequently need to add a new optional field with a non-empty default value. For example:
|
||||
|
||||
```typescript
|
||||
const orderParams = {
|
||||
let orderParams = {
|
||||
contactless_delivery: false
|
||||
};
|
||||
const order = api.createOrder(
|
||||
let order = api.createOrder(
|
||||
orderParams
|
||||
);
|
||||
```
|
||||
@@ -241,7 +241,7 @@ const order = api.createOrder(
|
||||
This new `contactless_delivery` option isn't required, but its default value is `true`. A question arises: how should developers discern the explicit intention to disable the option (`false`) from not knowing if it exists (the field isn't set)? They would have to write something like:
|
||||
|
||||
```typescript
|
||||
const value = orderParams.contactless_delivery;
|
||||
let value = orderParams.contactless_delivery;
|
||||
if (Type(value) == 'Boolean' && value == false) {
|
||||
…
|
||||
}
|
||||
@@ -254,10 +254,10 @@ If the protocol does not support resetting to default values as a first-class ci
|
||||
**Better**
|
||||
|
||||
```typescript
|
||||
const orderParams = {
|
||||
let orderParams = {
|
||||
/* <em> */force_contact_delivery: true/* </em> */
|
||||
};
|
||||
const order = api.createOrder(
|
||||
let order = api.createOrder(
|
||||
orderParams
|
||||
);
|
||||
```
|
||||
|
@@ -8,7 +8,7 @@ Let's proceed to the technical problems that API developers face. We begin with
|
||||
3. The client requests the current state of the system and gets an empty response as the initial request still hasn't reached the server:
|
||||
|
||||
```typescript
|
||||
const pendingOrders = await
|
||||
let pendingOrders = await
|
||||
api.getOngoingOrders(); // → []
|
||||
```
|
||||
|
||||
@@ -34,12 +34,12 @@ try {
|
||||
acquireLock(ORDER_CREATION);
|
||||
// Get the list of current orders
|
||||
// known to the system
|
||||
const pendingOrders = await
|
||||
let pendingOrders = await
|
||||
api.getPendingOrders();
|
||||
// If our order is absent,
|
||||
// create it
|
||||
if (pendingOrders.length == 0) {
|
||||
const order = await api
|
||||
let order = await api
|
||||
.createOrder(…)
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -63,17 +63,17 @@ Rather unsurprisingly, this approach sees very rare use in distributed client-se
|
||||
|
||||
```typescript
|
||||
// Retrieve the state
|
||||
const orderState =
|
||||
let orderState =
|
||||
await api.getOrderState();
|
||||
// The version is a part
|
||||
// of the state of the resource
|
||||
const version =
|
||||
let version =
|
||||
orderState.latestVersion;
|
||||
// An order might only be created
|
||||
// if the resource version hasn't
|
||||
// changed since the last read
|
||||
try {
|
||||
const task = await api
|
||||
let task = await api
|
||||
.createOrder(version, …);
|
||||
} catch (e) {
|
||||
// If the version is wrong, i.e.,
|
||||
|
@@ -5,15 +5,15 @@ The approach described in the previous chapter is in fact a trade-off: the API p
|
||||
```typescript
|
||||
// Reading the state,
|
||||
// possibly from a replica
|
||||
const orderState =
|
||||
let orderState =
|
||||
await api.getOrderState();
|
||||
const version =
|
||||
let version =
|
||||
orderState.latestVersion;
|
||||
try {
|
||||
// The request handler will
|
||||
// read the actual version
|
||||
// from the master data
|
||||
const task = await api
|
||||
let task = await api
|
||||
.createOrder(version, …);
|
||||
} catch (e) {
|
||||
…
|
||||
@@ -28,10 +28,10 @@ Choosing weak consistency instead of a strict one, however, brings some disadvan
|
||||
|
||||
```typescript
|
||||
// Creates an order
|
||||
const api = await api
|
||||
let api = await api
|
||||
.createOrder(…)
|
||||
// Returns a list of orders
|
||||
const pendingOrders = await api.
|
||||
let pendingOrders = await api.
|
||||
getOngoingOrders(); // → []
|
||||
// The list is empty
|
||||
```
|
||||
@@ -41,9 +41,9 @@ If strict consistency is not guaranteed, the second call might easily return an
|
||||
An important pattern that helps in this situation is implementing the “read-your-writes[ref Consistency Model. Read-Your-Writes Consistency](https://en.wikipedia.org/wiki/Consistency_model#Read-your-writes_consistency)” model, i.e., guaranteeing that clients observe the changes they have just made. The consistency might be lifted to the read-your-writes level by making clients pass some token that describes the last changes known to the client.
|
||||
|
||||
```typescript
|
||||
const order = await api
|
||||
let der = await api
|
||||
.createOrder(…);
|
||||
const pendingOrders = await api.
|
||||
let pendingOrders = await api.
|
||||
getOngoingOrders({
|
||||
…,
|
||||
// Pass the identifier of the
|
||||
|
@@ -7,10 +7,10 @@ We remember that this probability is equal to the ratio of time periods: getting
|
||||
Our usage scenario looks like this:
|
||||
|
||||
```typescript
|
||||
const pendingOrders = await api.
|
||||
let pendingOrders = await api.
|
||||
getOngoingOrders();
|
||||
if (pendingOrders.length == 0) {
|
||||
const order = await api
|
||||
let order = await api
|
||||
.createOrder(…);
|
||||
}
|
||||
```
|
||||
@@ -19,10 +19,10 @@ if (pendingOrders.length == 0) {
|
||||
// App restart happens here,
|
||||
// and all the same requests
|
||||
// are repeated
|
||||
const pendingOrders = await api.
|
||||
let pendingOrders = await api.
|
||||
getOngoingOrders(); // → []
|
||||
if (pendingOrders.length == 0) {
|
||||
const order = await api
|
||||
let order = await api
|
||||
.createOrder(…);
|
||||
}
|
||||
```
|
||||
@@ -36,12 +36,12 @@ However, what we could do to improve this timing remains unclear. Creating an or
|
||||
What could help us here is the asynchronous operations pattern. If our goal is to reduce the collision rate, there is no need to wait until the order is *actually* created as we need to quickly propagate the knowledge that the order is *accepted for creation*. We might employ the following technique: create *a task for order creation* and return its identifier, not the order itself.
|
||||
|
||||
```typescript
|
||||
const pendingOrders = await api.
|
||||
let pendingOrders = await api.
|
||||
getOngoingOrders();
|
||||
if (pendingOrders.length == 0) {
|
||||
// Instead of creating an order,
|
||||
// put the task for the creation
|
||||
const task = await api
|
||||
let task = await api
|
||||
.putOrderCreationTask(…);
|
||||
}
|
||||
```
|
||||
@@ -50,7 +50,7 @@ if (pendingOrders.length == 0) {
|
||||
// App restart happens here,
|
||||
// and all the same requests
|
||||
// are repeated
|
||||
const pendingOrders = await api.
|
||||
let pendingOrders = await api.
|
||||
getOngoingOrders();
|
||||
// → { tasks: [task] }
|
||||
```
|
||||
@@ -86,12 +86,12 @@ However, we must stress that excessive asynchronicity, though appealing to API d
|
||||
Therefore, despite all the advantages of the approach, we tend to recommend applying this pattern only to those cases when they are really needed (as in the example we started with when we needed to lower the probability of collisions) and having separate queues for each case. The perfect task queue solution is the one that doesn't look like a task queue. For example, we might simply make the “order creation task is accepted and awaits execution” state a separate order status and make its identifier the future identifier of the order itself:
|
||||
|
||||
```typescript
|
||||
const pendingOrders = await api.
|
||||
let pendingOrders = await api.
|
||||
getOngoingOrders();
|
||||
if (pendingOrders.length == 0) {
|
||||
// Don't call it a “task”,
|
||||
// just create an order
|
||||
const order = await api
|
||||
let order = await api
|
||||
.createOrder(…);
|
||||
}
|
||||
```
|
||||
@@ -100,7 +100,7 @@ if (pendingOrders.length == 0) {
|
||||
// App restart happens here,
|
||||
// and all the same requests
|
||||
// are repeated
|
||||
const pendingOrders = await api.
|
||||
let pendingOrders = await api.
|
||||
getOngoingOrders();
|
||||
/* → { orders: [{
|
||||
order_id: <task identifier>,
|
||||
|
@@ -3,7 +3,7 @@
|
||||
In the previous chapter, we concluded with the following interface that allows minimizing collisions while creating orders:
|
||||
|
||||
```typescript
|
||||
const pendingOrders = await api
|
||||
let pendingOrders = await api
|
||||
.getOngoingOrders();
|
||||
→
|
||||
{ orders: [{
|
||||
|
@@ -44,12 +44,12 @@ Now, let's consider a scenario where the partner receives an error from the API
|
||||
|
||||
```typescript
|
||||
// Retrieve the ongoing orders
|
||||
const pendingOrders = await api
|
||||
let pendingOrders = await api
|
||||
.getPendingOrders();
|
||||
// The partner checks the status of every
|
||||
// order in its system and prepares
|
||||
// the list of changes to perform
|
||||
const changes =
|
||||
let changes =
|
||||
await prepareStatusChanges(
|
||||
pendingOrders
|
||||
);
|
||||
@@ -80,7 +80,7 @@ Now, let's consider a scenario where the partner receives an error from the API
|
||||
2. Retrying only failed sub-requests:
|
||||
|
||||
```typescript
|
||||
const pendingOrders = await api
|
||||
let pendingOrders = await api
|
||||
.getPendingOrders();
|
||||
let changes =
|
||||
await prepareStatusChanges(
|
||||
@@ -115,9 +115,9 @@ Now, let's consider a scenario where the partner receives an error from the API
|
||||
|
||||
```typescript
|
||||
do {
|
||||
const pendingOrders = await api
|
||||
let pendingOrders = await api
|
||||
.getPendingOrders();
|
||||
const changes =
|
||||
let changes =
|
||||
await prepareStatusChanges(
|
||||
pendingOrders
|
||||
);
|
||||
|
@@ -25,17 +25,17 @@ However, there are also non-trivial problems we face while developing an SDK for
|
||||
|
||||
```typescript
|
||||
// Request 'lungo' and 'latte' recipes
|
||||
const recipes = await api
|
||||
let recipes = await api
|
||||
.getRecipes(['lungo', 'latte']);
|
||||
// Build a map that allows to quickly
|
||||
// find a recipe by its identifier
|
||||
const recipeMap = new Map();
|
||||
let recipeMap = new Map();
|
||||
recipes.forEach((recipe) => {
|
||||
recipeMap.set(recipe.id, recipe);
|
||||
});
|
||||
// Request offers for latte and lungo
|
||||
// in the vicinity
|
||||
const offers = await api.search({
|
||||
let offers = await api.search({
|
||||
recipes: ['lungo', 'latte'],
|
||||
location
|
||||
});
|
||||
@@ -47,7 +47,7 @@ However, there are also non-trivial problems we face while developing an SDK for
|
||||
promptUser(
|
||||
'What we have found',
|
||||
offers.map((offer) => {
|
||||
const recipe = recipeMap
|
||||
let recipe = recipeMap
|
||||
.get(offer.recipe_id);
|
||||
return {offer, recipe};
|
||||
}));
|
||||
@@ -57,11 +57,11 @@ However, there are also non-trivial problems we face while developing an SDK for
|
||||
|
||||
```typescript
|
||||
// Request 'lungo' and 'latte' recipes
|
||||
const recipes = await api
|
||||
let recipes = await api
|
||||
.getRecipes(['lungo', 'latte']);
|
||||
// Request offers for latte and lungo
|
||||
// in the vicinity
|
||||
const offers = await api.search({
|
||||
let offers = await api.search({
|
||||
// Pass the references to the recipes,
|
||||
// not their identifiers
|
||||
recipes,
|
||||
@@ -81,7 +81,7 @@ However, there are also non-trivial problems we face while developing an SDK for
|
||||
```typescript
|
||||
// Request offers for latte and lungo
|
||||
// in the vicinity
|
||||
const offers = await api.search({
|
||||
let offers = await api.search({
|
||||
recipes: ['lungo', 'latte'],
|
||||
location
|
||||
});
|
||||
@@ -103,10 +103,10 @@ However, there are also non-trivial problems we face while developing an SDK for
|
||||
|
||||
```typescript
|
||||
// Retrieve ongoing orders
|
||||
const orders = await api
|
||||
let orders = await api
|
||||
.getOngoingOrders();
|
||||
// Build order map
|
||||
const orderMap = new Map();
|
||||
let orderMap = new Map();
|
||||
orders.forEach((order) => {
|
||||
orderMap.set(order.id, order);
|
||||
});
|
||||
@@ -116,7 +116,7 @@ However, there are also non-trivial problems we face while developing an SDK for
|
||||
'order_state_change',
|
||||
(event) => {
|
||||
// Find the corresponding order
|
||||
const order = orderMap
|
||||
let order = orderMap
|
||||
.get(event.order_id);
|
||||
// Take some actions, like
|
||||
// updating the UI
|
||||
@@ -135,7 +135,7 @@ However, there are also non-trivial problems we face while developing an SDK for
|
||||
Once again, we face a situation where an SDK lacking important features leads to mistakes in applications that use it. It would be much more convenient for a developer if an order object allowed for subscribing to its status updates without the need to learn how it works at the transport level and how to avoid missing an event.
|
||||
|
||||
```typescript
|
||||
const order = await api
|
||||
let order = await api
|
||||
.createOrder(…)
|
||||
// No need to subscribe to
|
||||
// the entire status change
|
||||
@@ -152,9 +152,9 @@ However, there are also non-trivial problems we face while developing an SDK for
|
||||
|
||||
```typescript
|
||||
// Request offers
|
||||
const offers = await api.search(…);
|
||||
let offers = await api.search(…);
|
||||
// The user selects an offer
|
||||
const selectedOffer =
|
||||
let selectedOffer =
|
||||
await promptUser(offers);
|
||||
|
||||
let order;
|
||||
|
@@ -58,11 +58,11 @@ It is very easy to demonstrate how coupling several subject areas in one entity
|
||||
But it is not the end of the story. If the developer still wants exactly this, i.e., to show a coffee shop chain icon (if any) on the order creation button, then what should they do? Following the same logic, we should provide an even more specialized possibility to do so. For example, we can adopt the following logic: if there is a `createOrderButtonIconUrl` property in the data, the icon will be taken from this field. Developers could customize the order creation button by overwriting this `createOrderButtonIconUrl` field for every search result:
|
||||
|
||||
```typescript
|
||||
const searchBox = new SearchBox({
|
||||
let searchBox = new SearchBox({
|
||||
// For simplicity, let's allow
|
||||
// to override the search function
|
||||
searchFunction: function (params) {
|
||||
const res = await api.search(params);
|
||||
let res = await api.search(params);
|
||||
res.forEach(function (item) {
|
||||
item.createOrderButtonIconUrl =
|
||||
<the URL of the icon>;
|
||||
|
@@ -194,7 +194,7 @@ If we aren't making an SDK and have not had the task of making these components
|
||||
3. To implement new buttons, we can only propose to developers to create a custom offer list component (to provide methods for selecting previous and next offers) and a custom offer panel that will call these methods. If we find a simple solution for customizing, let's say, the “Place an order” button text, this solution needs to be supported in the `OfferList` code:
|
||||
|
||||
```typescript
|
||||
const searchBox = new SearchBox(…, {
|
||||
let searchBox = new SearchBox(…, {
|
||||
/* <em> */offerPanelCreateOrderButtonText:
|
||||
'Drink overpriced coffee!'/* </em> */
|
||||
});
|
||||
|
@@ -233,10 +233,10 @@ GET /coffee-machines/{id}/stocks
|
||||
Этот совет парадоксально противоположен предыдущему. Часто при разработке API возникает ситуация, когда добавляется новое необязательное поле с непустым значением по умолчанию. Например:
|
||||
|
||||
```typescript
|
||||
const orderParams = {
|
||||
let orderParams = {
|
||||
contactless_delivery: false
|
||||
};
|
||||
const order = api.createOrder(
|
||||
let order = api.createOrder(
|
||||
orderParams
|
||||
);
|
||||
```
|
||||
@@ -244,7 +244,7 @@ const order = api.createOrder(
|
||||
Новая опция `contactless_delivery` является необязательной, однако её значение по умолчанию — `true`. Возникает вопрос, каким образом разработчик должен отличить явное *нежелание* пользоваться опцией (`false`) от незнания о её существовании (поле не задано). Приходится писать что-то типа такого:
|
||||
|
||||
```typescript
|
||||
const value = orderParams.contactless_delivery;
|
||||
let value = orderParams.contactless_delivery;
|
||||
if (Type(value) == 'Boolean' && value == false) {
|
||||
…
|
||||
}
|
||||
@@ -257,10 +257,10 @@ if (Type(value) == 'Boolean' && value == false) {
|
||||
**Хорошо**
|
||||
|
||||
```typescript
|
||||
const orderParams = {
|
||||
let orderParams = {
|
||||
force_contact_delivery: true
|
||||
};
|
||||
const order = api.createOrder(
|
||||
let order = api.createOrder(
|
||||
orderParams
|
||||
);
|
||||
```
|
||||
|
@@ -8,7 +8,7 @@
|
||||
3. Клиент запрашивает текущее состояние системы и получает пустой ответ, поскольку таймаут случился раньше, чем запрос на создание заказа дошёл до сервера:
|
||||
|
||||
```typescript
|
||||
const pendingOrders = await
|
||||
let pendingOrders = await
|
||||
api.getOngoingOrders(); // → []
|
||||
```
|
||||
|
||||
@@ -34,12 +34,12 @@ try {
|
||||
lock = await api.acquireLock(ORDER_CREATION);
|
||||
// Получаем текущий список
|
||||
// заказов, известных системе
|
||||
const pendingOrders = await
|
||||
let pendingOrders = await
|
||||
api.getPendingOrders();
|
||||
// Если нашего заказа ещё нет,
|
||||
// создаём его
|
||||
if (pendingOrders.length == 0) {
|
||||
const order = await api.createOrder(…)
|
||||
let order = await api.createOrder(…)
|
||||
}
|
||||
} catch (e) {
|
||||
// Обработка ошибок
|
||||
@@ -62,17 +62,17 @@ try {
|
||||
|
||||
```typescript
|
||||
// Получаем состояние
|
||||
const orderState =
|
||||
let orderState =
|
||||
await api.getOrderState();
|
||||
// Частью состояния является
|
||||
// версия ресурса
|
||||
const version =
|
||||
let version =
|
||||
orderState.latestVersion;
|
||||
// Заказ можно создать,
|
||||
// только если версия состояния
|
||||
// не изменилась с момента чтения
|
||||
try {
|
||||
const task = await api
|
||||
let task = await api
|
||||
.createOrder(version, …);
|
||||
} catch (e) {
|
||||
// Если версия неверна, т.е. состояние
|
||||
|
@@ -5,16 +5,16 @@
|
||||
```typescript
|
||||
// Получаем состояние,
|
||||
// возможно, из реплики
|
||||
const orderState =
|
||||
let orderState =
|
||||
await api.getOrderState();
|
||||
const version =
|
||||
let version =
|
||||
orderState.latestVersion;
|
||||
try {
|
||||
// Обработчик запроса на
|
||||
// создание заказа прочитает
|
||||
// актуальную версию
|
||||
// из мастер-данных
|
||||
const task = await api
|
||||
let task = await api
|
||||
.createOrder(version, …);
|
||||
} catch (e) {
|
||||
…
|
||||
@@ -29,10 +29,10 @@ try {
|
||||
|
||||
```typescript
|
||||
// Создаёт заказ
|
||||
const api = await api
|
||||
let api = await api
|
||||
.createOrder(…)
|
||||
// Возвращает список заказов
|
||||
const pendingOrders = await api.
|
||||
let pendingOrders = await api.
|
||||
getOngoingOrders(); // → []
|
||||
// список пуст
|
||||
```
|
||||
@@ -42,9 +42,9 @@ const pendingOrders = await api.
|
||||
Важный паттерн, который поможет в этой ситуации — это имплементация модели «read-your-writes»[ref Consistency Model. Read-Your-Writes Consistency](https://en.wikipedia.org/wiki/Consistency_model#Read-your-writes_consistency), а именно гарантии, что клиент всегда «видит» те изменения, которые сам же и внёс. Поднять уровень слабой консистентности до read-your-writes можно, если предложить клиенту самому передать токен, описывающий его последние изменения.
|
||||
|
||||
```typescript
|
||||
const order = await api
|
||||
let order = await api
|
||||
.createOrder(…);
|
||||
const pendingOrders = await api.
|
||||
let pendingOrders = await api.
|
||||
getOngoingOrders({
|
||||
…,
|
||||
// Передаём идентификатор
|
||||
|
@@ -7,19 +7,19 @@
|
||||
Наш сценарий использования, напомним, выглядит так:
|
||||
|
||||
```typescript
|
||||
const pendingOrders = await api.
|
||||
let pendingOrders = await api.
|
||||
getOngoingOrders();
|
||||
if (pendingOrder.length == 0) {
|
||||
const order = await api
|
||||
let order = await api
|
||||
.createOrder(…);
|
||||
}
|
||||
// Здесь происходит крэш приложения,
|
||||
// и те же операции выполняются
|
||||
// повторно
|
||||
const pendingOrders = await api.
|
||||
let pendingOrders = await api.
|
||||
getOngoingOrders(); // → []
|
||||
if (pendingOrder.length == 0) {
|
||||
const order = await api
|
||||
let order = await api
|
||||
.createOrder(…);
|
||||
}
|
||||
```
|
||||
@@ -33,18 +33,18 @@ if (pendingOrder.length == 0) {
|
||||
Здесь нам на помощь приходят асинхронные вызовы. Если наша цель — уменьшить число коллизий, то нам нет никакой нужды дожидаться, когда заказ будет *действительно* создан; наша цель — максимально быстро распространить по репликам знание о том, что заказ *принят к созданию*. Мы можем поступить следующим образом: создавать не заказ, а задание на создание заказа, и возвращать его идентификатор.
|
||||
|
||||
```typescript
|
||||
const pendingOrders = await api.
|
||||
let pendingOrders = await api.
|
||||
getOngoingOrders();
|
||||
if (pendingOrder.length == 0) {
|
||||
// Вместо создания заказа
|
||||
// размещаем задание на создание
|
||||
const task = await api
|
||||
let task = await api
|
||||
.putOrderCreationTask(…);
|
||||
}
|
||||
// Здесь происходит крэш приложения,
|
||||
// и те же операции выполняются
|
||||
// повторно
|
||||
const pendingOrders = await api.
|
||||
let pendingOrders = await api.
|
||||
getOngoingOrders();
|
||||
// → { tasks: [task] }
|
||||
```
|
||||
@@ -80,18 +80,18 @@ const pendingOrders = await api.
|
||||
Поэтому, при всей привлекательности идеи, мы всё же склонны рекомендовать ограничиться асинхронными интерфейсами только там, где они действительно критически важны (как в примере выше, где они снижают вероятность коллизий), и при этом иметь отдельные очереди для каждого кейса. Идеальное решение с очередями — то, которое вписано в бизнес-логику и вообще не выглядит очередью. Например, ничто не мешает нам объявить состояние «задание на создание заказа принято и ожидает исполнения» просто отдельным статусом заказа, а его идентификатор сделать идентификатором будущего заказа:
|
||||
|
||||
```typescript
|
||||
const pendingOrders = await api.
|
||||
let pendingOrders = await api.
|
||||
getOngoingOrders();
|
||||
if (pendingOrder.length == 0) {
|
||||
// Не называем это «заданием» —
|
||||
// просто создаём заказ
|
||||
const order = await api
|
||||
let order = await api
|
||||
.createOrder(…);
|
||||
}
|
||||
// Здесь происходит крэш приложения,
|
||||
// и те же операции выполняются
|
||||
// повторно
|
||||
const pendingOrders = await api.
|
||||
let pendingOrders = await api.
|
||||
getOngoingOrders();
|
||||
/* → { orders: [{
|
||||
order_id: <идентификатор задания>,
|
||||
|
@@ -3,7 +3,7 @@
|
||||
В предыдущей главе мы пришли вот к такому интерфейсу, позволяющему минимизировать коллизии при создании заказов:
|
||||
|
||||
```typescript
|
||||
const pendingOrders = await api
|
||||
let pendingOrders = await api
|
||||
.getOngoingOrders();
|
||||
→
|
||||
{ orders: [{
|
||||
|
@@ -42,13 +42,13 @@ POST /v1/orders/bulk-status-change
|
||||
|
||||
```typescript
|
||||
// Получаем все текущие заказы
|
||||
const pendingOrders = await api
|
||||
let pendingOrders = await api
|
||||
.getPendingOrders();
|
||||
// Партнёр проверяет статус
|
||||
// каждого из них в своей
|
||||
// системе и готовит
|
||||
// необходимые изменения
|
||||
const changes =
|
||||
let changes =
|
||||
await prepareStatusChanges(
|
||||
pendingOrders
|
||||
);
|
||||
@@ -79,7 +79,7 @@ POST /v1/orders/bulk-status-change
|
||||
2. Повтор только неудавшихся подзапросов:
|
||||
|
||||
```typescript
|
||||
const pendingOrders = await api
|
||||
let pendingOrders = await api
|
||||
.getPendingOrders();
|
||||
let changes =
|
||||
await prepareStatusChanges(
|
||||
@@ -115,9 +115,9 @@ POST /v1/orders/bulk-status-change
|
||||
|
||||
```typescript
|
||||
do {
|
||||
const pendingOrders = await api
|
||||
let pendingOrders = await api
|
||||
.getPendingOrders();
|
||||
const changes =
|
||||
let changes =
|
||||
await prepareStatusChanges(
|
||||
pendingOrders
|
||||
);
|
||||
|
@@ -24,17 +24,17 @@
|
||||
```typescript
|
||||
// Запрашиваем информацию о рецептах
|
||||
// лунго и латте
|
||||
const recipes = await api
|
||||
let recipes = await api
|
||||
.getRecipes(['lungo', 'latte']);
|
||||
// Строим карту, позволяющую обратиться
|
||||
// к данным о рецепте по его id
|
||||
const recipeMap = new Map();
|
||||
let recipeMap = new Map();
|
||||
recipes.forEach((recipe) => {
|
||||
recipeMap.set(recipe.id, recipe);
|
||||
});
|
||||
// Запрашиваем предложения
|
||||
// лунго и латте
|
||||
const offers = await api.search({
|
||||
let offers = await api.search({
|
||||
recipes: ['lungo', 'latte'],
|
||||
location
|
||||
});
|
||||
@@ -45,7 +45,7 @@
|
||||
promptUser(
|
||||
'Найденные предложения',
|
||||
offers.map((offer) => {
|
||||
const recipe = recipeMap
|
||||
let recipe = recipeMap
|
||||
.get(offer.recipe_id);
|
||||
return {offer, recipe};
|
||||
})
|
||||
@@ -57,11 +57,11 @@
|
||||
```typescript
|
||||
// Запрашиваем информацию о рецептах
|
||||
// лунго и латте
|
||||
const recipes = await api
|
||||
let recipes = await api
|
||||
.getRecipes(['lungo', 'latte']);
|
||||
// Запрашиваем предложения
|
||||
// лунго и латте
|
||||
const offers = await api.search({
|
||||
let offers = await api.search({
|
||||
// Передаём не идентификаторы
|
||||
// рецептов, а ссылки на объекты,
|
||||
// описывающие рецепты
|
||||
@@ -82,7 +82,7 @@
|
||||
```typescript
|
||||
// Запрашиваем предложения
|
||||
// лунго и латте
|
||||
const offers = await api.search({
|
||||
let offers = await api.search({
|
||||
recipes: ['lungo', 'latte'],
|
||||
location
|
||||
});
|
||||
@@ -104,10 +104,10 @@
|
||||
|
||||
```typescript
|
||||
// Получаем текущие заказы
|
||||
const orders = await api
|
||||
let orders = await api
|
||||
.getOngoingOrders();
|
||||
// Строим карту заказов
|
||||
const orderMap = new Map();
|
||||
let orderMap = new Map();
|
||||
orders.forEach((order) => {
|
||||
orderMap.set(order.id, order);
|
||||
});
|
||||
@@ -116,7 +116,7 @@
|
||||
api.subscribe(
|
||||
'order_state_change',
|
||||
(event) => {
|
||||
const order = orderMap
|
||||
let order = orderMap
|
||||
.get(event.order_id);
|
||||
// Выполняем какие-то
|
||||
// действия с заказом,
|
||||
@@ -136,7 +136,7 @@
|
||||
И вновь мы приходим к тому, что недостаточно продуманный SDK приводит к ошибкам в работе использующих его приложений. Разработчику было бы намного удобнее, если бы объект заказа позволял подписаться на свои события, не задумываясь о том, как эта подписка технически работает и как не пропустить события:
|
||||
|
||||
```typescript
|
||||
const order = await api
|
||||
let order = await api
|
||||
.createOrder(…)
|
||||
// Нет нужды подписываться
|
||||
// на *все* события и потом
|
||||
@@ -153,10 +153,10 @@
|
||||
|
||||
```typescript
|
||||
// Получаем предложения
|
||||
const offers = await api.search(…);
|
||||
let offers = await api.search(…);
|
||||
// Пользователь выбирает
|
||||
// подходящее предложение
|
||||
const selectedOffer = await promptUser(offers);
|
||||
let selectedOffer = await promptUser(offers);
|
||||
let order;
|
||||
let offer = selectedOffer;
|
||||
let numberOfTries = 0;
|
||||
|
@@ -58,11 +58,11 @@
|
||||
Но на этом история не заканчивается. Если разработчик всё-таки хочет именно этого, т.е. показывать иконку сети кофеен (если она есть) на кнопке создания заказа — как ему это сделать? Из той же логики, нам необходимо предоставить ещё более частную возможность такого переопределения. Например, представим себе следующую функциональность: если в данных предложения есть поле `createOrderButtonIconUrl`, то иконка будет взята из этого поля. Тогда разработчик сможет кастомизировать кнопку заказа, подменив в данных поле `createOrderButtonIconUrl` для каждого результата поиска:
|
||||
|
||||
```typescript
|
||||
const searchBox = new SearchBox({
|
||||
let searchBox = new SearchBox({
|
||||
// Предположим, что мы разрешили
|
||||
// переопределять поисковую функцию
|
||||
searchFunction: function (params) {
|
||||
const res = await api.search(params);
|
||||
let res = await api.search(params);
|
||||
res.forEach(function (item) {
|
||||
item.createOrderButtonIconUrl =
|
||||
<URL нужной иконки>;
|
||||
|
@@ -192,7 +192,7 @@ interface IOfferPanel {
|
||||
3. Для реализации новых кнопок мы можем только лишь предложить программисту реализовать свой список предложений (чтобы предоставить методы выбора предыдущего / следующего предложения) и свою панель предложений, которая эти методы будет вызывать. Даже если мы придумаем какой-нибудь простой способ кастомизировать, например, текст кнопки «Сделать заказ», его поддержка всё равно будет ответственностью компонента `OfferList`:
|
||||
|
||||
```typescript
|
||||
const searchBox = new SearchBox(…, {
|
||||
let searchBox = new SearchBox(…, {
|
||||
/* <em> */offerPanelCreateOrderButtonText:
|
||||
'Drink overpriced coffee!'/* </em> */
|
||||
});
|
||||
|
Reference in New Issue
Block a user