Align Get Menu with v2 endpoint, menu_type, and gzip
This commit is contained in:
parent
0c41ad5858
commit
5ea2d86a48
@ -6,6 +6,9 @@ Source checked: Uber Eats "Menu Integration" section shared by you.
|
|||||||
|
|
||||||
- Retrieve menu:
|
- Retrieve menu:
|
||||||
- `GET /api/v1/uber/menu`
|
- `GET /api/v1/uber/menu`
|
||||||
|
- aligned to upstream `GET /v2/eats/stores/{store_id}/menus`
|
||||||
|
- supports `menu_type` query values for delivery/pick-up/dine-in
|
||||||
|
- applies `Accept-Encoding: gzip` for large payload responses
|
||||||
- Full menu upload/replace:
|
- Full menu upload/replace:
|
||||||
- `PUT /api/v1/uber/menu/replace` (primary)
|
- `PUT /api/v1/uber/menu/replace` (primary)
|
||||||
- Individual item updates:
|
- Individual item updates:
|
||||||
@ -22,4 +25,3 @@ Source checked: Uber Eats "Menu Integration" section shared by you.
|
|||||||
- Strict typed schemas for full menu payload entities (item, modifier group, category, menu)
|
- Strict typed schemas for full menu payload entities (item, modifier group, category, menu)
|
||||||
- Validation rules for image metadata limits and alcoholic item classifications
|
- Validation rules for image metadata limits and alcoholic item classifications
|
||||||
- Dedicated mapper helpers for `core_price` and `bundled_items` enrichment
|
- Dedicated mapper helpers for `core_price` and `bundled_items` enrichment
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,16 @@ Item update route:
|
|||||||
|
|
||||||
- `POST /api/v1/uber/menu/items`
|
- `POST /api/v1/uber/menu/items`
|
||||||
|
|
||||||
|
Menu fetch route:
|
||||||
|
|
||||||
|
- `GET /api/v1/uber/menu`
|
||||||
|
- upstream mapped to `GET /v2/eats/stores/{store_id}/menus`
|
||||||
|
- supports query `menu_type`:
|
||||||
|
- `MENU_TYPE_FULFILLMENT_DELIVERY` (default)
|
||||||
|
- `MENU_TYPE_FULFILLMENT_PICK_UP`
|
||||||
|
- `MENU_TYPE_FULFILLMENT_DINE_IN`
|
||||||
|
- sends `Accept-Encoding: gzip` upstream for large menu payloads
|
||||||
|
|
||||||
Best-practice note:
|
Best-practice note:
|
||||||
|
|
||||||
- Use API-managed menus only for integrated stores (avoid manual Menu Maker edits to prevent drift).
|
- Use API-managed menus only for integrated stores (avoid manual Menu Maker edits to prevent drift).
|
||||||
|
|||||||
@ -335,6 +335,38 @@
|
|||||||
"tags": [
|
"tags": [
|
||||||
"Uber Menu"
|
"Uber Menu"
|
||||||
],
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "merchantId",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "storeId",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "menu_type",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"MENU_TYPE_FULFILLMENT_DELIVERY",
|
||||||
|
"MENU_TYPE_FULFILLMENT_PICK_UP",
|
||||||
|
"MENU_TYPE_FULFILLMENT_DINE_IN"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"description": "Defaults to MENU_TYPE_FULFILLMENT_DELIVERY."
|
||||||
|
}
|
||||||
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Menu fetched"
|
"description": "Menu fetched"
|
||||||
|
|||||||
@ -232,6 +232,44 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Get Menu (v2)",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "x-api-key",
|
||||||
|
"value": "{{apiKey}}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"url": {
|
||||||
|
"raw": "{{baseUrl}}/api/v1/uber/menu?merchantId={{merchantId}}&storeId={{storeId}}&menu_type=MENU_TYPE_FULFILLMENT_DELIVERY",
|
||||||
|
"host": [
|
||||||
|
"{{baseUrl}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"v1",
|
||||||
|
"uber",
|
||||||
|
"menu"
|
||||||
|
],
|
||||||
|
"query": [
|
||||||
|
{
|
||||||
|
"key": "merchantId",
|
||||||
|
"value": "{{merchantId}}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "storeId",
|
||||||
|
"value": "{{storeId}}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "menu_type",
|
||||||
|
"value": "MENU_TYPE_FULFILLMENT_DELIVERY"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "List Provisionable Stores",
|
"name": "List Provisionable Stores",
|
||||||
"request": {
|
"request": {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
menu: {
|
menu: {
|
||||||
upsert: "/v1/eats/stores/{storeId}/menus",
|
upsert: "/v1/eats/stores/{storeId}/menus",
|
||||||
get: "/v1/eats/stores/{storeId}/menus",
|
get: "/v2/eats/stores/{storeId}/menus",
|
||||||
itemsUpdate: "/v1/eats/stores/{storeId}/menus/items"
|
itemsUpdate: "/v1/eats/stores/{storeId}/menus/items"
|
||||||
},
|
},
|
||||||
orders: {
|
orders: {
|
||||||
|
|||||||
@ -35,10 +35,28 @@ async function upsertMenu(req, res) {
|
|||||||
async function getMenu(req, res) {
|
async function getMenu(req, res) {
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
merchantId: z.string().min(1),
|
merchantId: z.string().min(1),
|
||||||
storeId: z.string().min(1)
|
storeId: z.string().min(1),
|
||||||
|
menu_type: z
|
||||||
|
.enum([
|
||||||
|
"MENU_TYPE_FULFILLMENT_DELIVERY",
|
||||||
|
"MENU_TYPE_FULFILLMENT_PICK_UP",
|
||||||
|
"MENU_TYPE_FULFILLMENT_DINE_IN"
|
||||||
|
])
|
||||||
|
.optional(),
|
||||||
|
menuType: z
|
||||||
|
.enum([
|
||||||
|
"MENU_TYPE_FULFILLMENT_DELIVERY",
|
||||||
|
"MENU_TYPE_FULFILLMENT_PICK_UP",
|
||||||
|
"MENU_TYPE_FULFILLMENT_DINE_IN"
|
||||||
|
])
|
||||||
|
.optional()
|
||||||
});
|
});
|
||||||
const payload = schema.parse(req.query);
|
const payload = schema.parse(req.query);
|
||||||
const data = await proxyService.menuGet(payload);
|
const data = await proxyService.menuGet({
|
||||||
|
merchantId: payload.merchantId,
|
||||||
|
storeId: payload.storeId,
|
||||||
|
menuType: payload.menu_type || payload.menuType
|
||||||
|
});
|
||||||
return res.json({ success: true, data });
|
return res.json({ success: true, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -55,7 +55,17 @@ async function resolveAuthToken({ authMode = "app", merchantId, scopes }) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function callUberApi({ merchantId, method, uberPath, query, body, wrapperRoute, authMode, scopes }) {
|
async function callUberApi({
|
||||||
|
merchantId,
|
||||||
|
method,
|
||||||
|
uberPath,
|
||||||
|
query,
|
||||||
|
body,
|
||||||
|
wrapperRoute,
|
||||||
|
authMode,
|
||||||
|
scopes,
|
||||||
|
headers
|
||||||
|
}) {
|
||||||
const resolvedAuth = await resolveAuthToken({ authMode, merchantId, scopes });
|
const resolvedAuth = await resolveAuthToken({ authMode, merchantId, scopes });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -68,7 +78,8 @@ async function callUberApi({ merchantId, method, uberPath, query, body, wrapperR
|
|||||||
data: body,
|
data: body,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: buildAuthHeader(resolvedAuth.tokenType, resolvedAuth.accessToken),
|
Authorization: buildAuthHeader(resolvedAuth.tokenType, resolvedAuth.accessToken),
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json",
|
||||||
|
...(headers || {})
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
maxAttempts: 4,
|
maxAttempts: 4,
|
||||||
@ -148,12 +159,18 @@ async function menuReplace({ merchantId, storeId, payload }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function menuGet({ merchantId, storeId }) {
|
async function menuGet({ merchantId, storeId, menuType }) {
|
||||||
const uberPath = interpolatePath(uberEndpoints.menu.get, { storeId });
|
const uberPath = interpolatePath(uberEndpoints.menu.get, { storeId });
|
||||||
return callUberApi({
|
return callUberApi({
|
||||||
merchantId,
|
merchantId,
|
||||||
method: "GET",
|
method: "GET",
|
||||||
uberPath,
|
uberPath,
|
||||||
|
query: {
|
||||||
|
menu_type: menuType || "MENU_TYPE_FULFILLMENT_DELIVERY"
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
"Accept-Encoding": "gzip"
|
||||||
|
},
|
||||||
wrapperRoute: "/api/v1/uber/menu",
|
wrapperRoute: "/api/v1/uber/menu",
|
||||||
authMode: "app",
|
authMode: "app",
|
||||||
scopes: AUTH_SCOPES.STORE
|
scopes: AUTH_SCOPES.STORE
|
||||||
|
|||||||
@ -63,6 +63,27 @@ router.post("/uber/menu/items", asyncHandler(controller.updateMenuItems));
|
|||||||
* summary: Fetch store menu
|
* summary: Fetch store menu
|
||||||
* tags:
|
* tags:
|
||||||
* - Uber Menu
|
* - Uber Menu
|
||||||
|
* parameters:
|
||||||
|
* - in: query
|
||||||
|
* name: merchantId
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* - in: query
|
||||||
|
* name: storeId
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* - in: query
|
||||||
|
* name: menu_type
|
||||||
|
* required: false
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* enum:
|
||||||
|
* - MENU_TYPE_FULFILLMENT_DELIVERY
|
||||||
|
* - MENU_TYPE_FULFILLMENT_PICK_UP
|
||||||
|
* - MENU_TYPE_FULFILLMENT_DINE_IN
|
||||||
|
* description: Defaults to MENU_TYPE_FULFILLMENT_DELIVERY.
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Menu fetched
|
* description: Menu fetched
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user