-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpartner-stores.openapi.yaml
More file actions
335 lines (319 loc) · 15.6 KB
/
Copy pathpartner-stores.openapi.yaml
File metadata and controls
335 lines (319 loc) · 15.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
openapi: 3.0.3
info:
title: Partner Stores API
version: 1.0.0
description: |
Контракт экрана «Выберите магазин» мобильного приложения «Петрушка Зеленая».
Состав полей выведен из макета: заголовок экрана, список плашек магазинов,
у каждой плашки название, логотип, строка доставки и переход на внешний ресурс.
servers:
- url: https://api.petrushka-zelenaya.example
paths:
/api/v1/mobile/partner-stores:
get:
summary: Получить список магазинов-партнеров для экрана выбора магазина
operationId: listPartnerStores
description: |
Вызывается при открытии экрана.
Допущение: в исходном задании местоположение не упомянуто, но сроки доставки
в макете зависят от зоны, поэтому запрос должен содержать хотя бы один из
параметров `address_id` или `city_id`. Если переданы оба, приоритет у `address_id`.
Если не передан ни один, сервер отвечает `400 LOCATION_REQUIRED`. Оба параметра
объявлены необязательными, потому что OpenAPI 3.0 не выражает условие «хотя бы один»
на уровне схемы; правило проверяется на сервере и описано здесь.
Экран доступен и без авторизации. Если передан валидный токен, сервер может
сам подставить сохраненный `address_id` пользователя, и тогда ни один параметр
местоположения передавать не нужно.
security:
- {}
- bearerAuth: []
parameters:
- name: address_id
in: query
required: false
description: |
Сохраненный адрес пользователя. Если передан, сроки доставки считаются
по нему и он имеет приоритет над `city_id`.
schema:
type: string
example: addr-77021
- name: city_id
in: query
required: false
description: |
Город или зона доставки. Используется, если у пользователя еще нет
сохраненного адреса.
schema:
type: string
example: moscow
- name: Accept-Language
in: header
required: false
description: Язык названий, подписей и текстов доставки.
schema:
type: string
default: ru-RU
- name: X-Timezone
in: header
required: false
description: |
Часовой пояс устройства в формате IANA.
Источником истины для границ слотов, поля `slot.date` и слов
«сегодня»/«завтра» служит часовой пояс зоны доставки, а не устройства.
Этот заголовок используется только для сравнения: если пояс устройства
отличается от пояса зоны, относительные слова не применяются и в `value`
возвращается явная дата, потому что «сегодня» на часах пользователя
и «сегодня» в зоне доставки могут быть разными днями. Если заголовок
не передан, сервер считает пояса совпадающими.
schema:
type: string
example: Europe/Moscow
responses:
'200':
description: Заголовок экрана и упорядоченный список плашек магазинов.
content:
application/json:
schema:
$ref: '#/components/schemas/PartnerStoresResponse'
'400':
description: |
Не передан ни `address_id`, ни `city_id`, и местоположение не удалось
определить по токену (`LOCATION_REQUIRED`), либо переданное значение
некорректно (`INVALID_PARAMETER`).
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
description: Внутренняя ошибка сервиса.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: |
Необязательная авторизация. Токен нужен только для того, чтобы сервер
подставил сохраненный адрес пользователя вместо явного параметра
местоположения. Список партнеров от авторизации не зависит.
schemas:
PartnerStoresResponse:
type: object
required: [data, meta]
properties:
data:
type: object
required: [screen_title, items]
properties:
screen_title:
type: string
description: Заголовок экрана из макета.
example: Выберите магазин
items:
type: array
description: Порядок элементов массива определяет порядок плашек на экране.
items:
$ref: '#/components/schemas/PartnerStore'
meta:
$ref: '#/components/schemas/Meta'
PartnerStore:
type: object
description: |
Плашка магазина. Поле `delivery` отсутствует, если для зоны срок неизвестен:
тогда плашка показывается без строки доставки. Отсутствие поля предпочтительнее
`null`, потому что не требует от клиента различать «нет данных» и «пустая строка».
required: [id, name, logo, external_url, open_mode]
properties:
id:
type: string
example: partner-metro
name:
type: string
minLength: 1
maxLength: 120
description: Название магазина. Основной видимый текст плашки.
example: METRO
logo:
$ref: '#/components/schemas/Logo'
delivery:
$ref: '#/components/schemas/Delivery'
external_url:
type: string
format: uri
description: Серверная allowlist-проверенная HTTPS-ссылка на внешний ресурс.
example: https://partner.example/metro
open_mode:
type: string
enum: [external_browser]
description: |
Способ открытия ссылки при нажатии на плашку. Сейчас допустимо одно
значение: макет ведет пользователя на внешний ресурс партнера, и такие
ссылки открываются в системном браузере, а не во встроенном webview.
Поле объявлено перечислением, а не булевым флагом, чтобы добавление
следующего способа (например, `in_app_webview` для партнеров с
собственной витриной внутри приложения) не ломало контракт: клиент
обязан игнорировать плашку с неизвестным ему `open_mode`.
example: external_browser
Logo:
type: object
required: [url]
properties:
url:
type: string
format: uri
example: https://cdn.petrushka-zelenaya.example/partners/metro/logo.png
background_color:
type: string
pattern: '^#[0-9A-Fa-f]{6}$'
description: |
Цвет подложки логотипа в макете (у METRO темно-синяя, у «Виктории» салатовая,
у «Ашана» и «ВкусВилла» белая). Если поле не передано, клиент использует
подложку по умолчанию из темы приложения.
example: '#0A2C6E'
Delivery:
description: |
Одна строка доставки в двух вариантах из макета:
`NEAREST_SLOT` - «Ближайшая доставка / сегодня 21:00-23:00»,
`EXPRESS` - «Быстрая доставка / от 20 до 60 минут».
Варианты разведены через `oneOf`; `kind` служит дискриминатором для кодогенераторов.
Запрет на смешивание вариантов дает не сам `oneOf`, а `not: required` внутри каждого
варианта: без него объект с `kind: NEAREST_SLOT` и лишним полем `express` прошел бы
валидацию, потому что `additionalProperties` не наследуется через `allOf`.
oneOf:
- $ref: '#/components/schemas/DeliveryNearestSlot'
- $ref: '#/components/schemas/DeliveryExpress'
discriminator:
propertyName: kind
mapping:
NEAREST_SLOT: '#/components/schemas/DeliveryNearestSlot'
EXPRESS: '#/components/schemas/DeliveryExpress'
DeliveryLineBase:
type: object
description: |
Общая часть строки доставки. `label` и `value` - готовый к отображению текст;
машинные значения варианта передаются рядом для сортировки, аналитики
и локальной перепроверки на клиенте.
required: [kind, label, value, style]
properties:
kind:
type: string
enum: [NEAREST_SLOT, EXPRESS]
label:
type: string
description: Первая строка подписи.
example: Ближайшая доставка
value:
type: string
description: Вторая строка подписи, выделенная жирным в макете.
example: сегодня 21:00-23:00
style:
type: string
enum: [DEFAULT, ACCENT]
description: |
Оформление строки. `ACCENT` - выделение акцентным цветом,
в макете так показана быстрая доставка «ВкусВилла».
Цвет задается темой клиента, сервер не передает hex.
example: DEFAULT
DeliveryNearestSlot:
description: Ближайший слот доставки. Поле `express` в этом варианте передавать нельзя.
allOf:
- $ref: '#/components/schemas/DeliveryLineBase'
- type: object
required: [slot]
not:
required: [express]
properties:
kind:
type: string
enum: [NEAREST_SLOT]
slot:
$ref: '#/components/schemas/DeliverySlot'
DeliveryExpress:
description: Экспресс-доставка. Поле `slot` в этом варианте передавать нельзя.
allOf:
- $ref: '#/components/schemas/DeliveryLineBase'
- type: object
required: [express]
not:
required: [slot]
properties:
kind:
type: string
enum: [EXPRESS]
express:
$ref: '#/components/schemas/DeliveryExpressRange'
DeliverySlot:
type: object
description: |
Инварианты, которые схема выразить не может и которые проверяет сервер:
`starts_at < ends_at`; оба момента лежат в пределах календарной даты `date`
в часовом поясе зоны доставки; `ends_at` строго больше `meta.generated_at`,
то есть истекший слот не возвращается - ближайшим становится следующий
доступный, при необходимости на завтра.
required: [date, starts_at, ends_at]
properties:
date:
type: string
format: date
description: Дата слота в часовом поясе зоны доставки.
example: '2026-07-09'
starts_at:
type: string
format: date-time
example: '2026-07-09T18:00:00Z'
ends_at:
type: string
format: date-time
example: '2026-07-09T20:00:00Z'
DeliveryExpressRange:
type: object
description: |
Инвариант `min_minutes <= max_minutes` схема выразить не может;
его проверяет сервер при формировании ответа.
required: [min_minutes, max_minutes]
properties:
min_minutes:
type: integer
minimum: 1
example: 20
max_minutes:
type: integer
minimum: 1
description: Не меньше `min_minutes`.
example: 60
Meta:
type: object
required: [request_id, generated_at]
properties:
request_id:
type: string
example: req-01J7EXAMPLE9K2
generated_at:
type: string
format: date-time
description: |
Момент расчета сроков доставки. Все возвращенные слоты заканчиваются позже
этого момента. Клиент перезапрашивает экран, если данные устарели.
example: '2026-07-09T09:00:00Z'
ErrorResponse:
type: object
required: [error]
properties:
error:
type: object
required: [code, message, request_id]
properties:
code:
type: string
enum: [LOCATION_REQUIRED, INVALID_PARAMETER, INTERNAL_ERROR]
example: LOCATION_REQUIRED
message:
type: string
example: Требуется передать address_id или city_id.
request_id:
type: string
example: req-01J7EXAMPLE9K2