Align Get Menu with v2 endpoint, menu_type, and gzip

This commit is contained in:
MOHAN 2026-03-29 18:34:56 +05:30
parent 0c41ad5858
commit 5ea2d86a48
8 changed files with 145 additions and 7 deletions

View File

@ -6,6 +6,9 @@ Source checked: Uber Eats "Menu Integration" section shared by you.
- Retrieve 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:
- `PUT /api/v1/uber/menu/replace` (primary)
- 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)
- Validation rules for image metadata limits and alcoholic item classifications
- Dedicated mapper helpers for `core_price` and `bundled_items` enrichment

View File

@ -16,6 +16,16 @@ Item update route:
- `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:
- Use API-managed menus only for integrated stores (avoid manual Menu Maker edits to prevent drift).

View File

@ -335,6 +335,38 @@
"tags": [
"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": {
"200": {
"description": "Menu fetched"

View File

@ -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",
"request": {

View File

@ -1,7 +1,7 @@
module.exports = {
menu: {
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"
},
orders: {

View File

@ -35,10 +35,28 @@ async function upsertMenu(req, res) {
async function getMenu(req, res) {
const schema = z.object({
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 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 });
}

View File

@ -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 });
try {
@ -68,7 +78,8 @@ async function callUberApi({ merchantId, method, uberPath, query, body, wrapperR
data: body,
headers: {
Authorization: buildAuthHeader(resolvedAuth.tokenType, resolvedAuth.accessToken),
"Content-Type": "application/json"
"Content-Type": "application/json",
...(headers || {})
}
}),
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 });
return callUberApi({
merchantId,
method: "GET",
uberPath,
query: {
menu_type: menuType || "MENU_TYPE_FULFILLMENT_DELIVERY"
},
headers: {
"Accept-Encoding": "gzip"
},
wrapperRoute: "/api/v1/uber/menu",
authMode: "app",
scopes: AUTH_SCOPES.STORE

View File

@ -63,6 +63,27 @@ router.post("/uber/menu/items", asyncHandler(controller.updateMenuItems));
* summary: Fetch store menu
* tags:
* - 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:
* 200:
* description: Menu fetched