feat: implement menu items update endpoint and align menu integration routes
This commit is contained in:
parent
1474781c08
commit
8fb333918c
25
docs/developer-portal/05-menu-integration-audit.md
Normal file
25
docs/developer-portal/05-menu-integration-audit.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# 05 Menu Integration Audit
|
||||||
|
|
||||||
|
Source checked: Uber Eats "Menu Integration" section shared by you.
|
||||||
|
|
||||||
|
## Implemented Now
|
||||||
|
|
||||||
|
- Retrieve menu:
|
||||||
|
- `GET /api/v1/uber/menu`
|
||||||
|
- Full menu upload/replace:
|
||||||
|
- `PUT /api/v1/uber/menu/replace` (primary)
|
||||||
|
- Individual item updates:
|
||||||
|
- `POST /api/v1/uber/menu/items`
|
||||||
|
- supports stock/price style item-level updates via Menu Items endpoint
|
||||||
|
|
||||||
|
## Existing Before
|
||||||
|
|
||||||
|
- Legacy menu upsert helper route (`POST /api/v1/uber/menu/upsert`)
|
||||||
|
- Store/menu module structure and app-token auth for menu calls
|
||||||
|
|
||||||
|
## Pending
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
@ -4,9 +4,18 @@ Menu sync between POS and Uber Eats:
|
|||||||
|
|
||||||
- Full menu replacement via PUT
|
- Full menu replacement via PUT
|
||||||
- Fetch menu from Uber
|
- Fetch menu from Uber
|
||||||
|
- Item-level updates (out-of-stock/price updates)
|
||||||
- Item and modifier mapping strategy
|
- Item and modifier mapping strategy
|
||||||
- Validation and publish error handling
|
- Validation and publish error handling
|
||||||
|
|
||||||
Current wrapper route for full replacement:
|
Current wrapper route for full replacement:
|
||||||
|
|
||||||
- `PUT /api/v1/uber/menu/replace`
|
- `PUT /api/v1/uber/menu/replace`
|
||||||
|
|
||||||
|
Item update route:
|
||||||
|
|
||||||
|
- `POST /api/v1/uber/menu/items`
|
||||||
|
|
||||||
|
Best-practice note:
|
||||||
|
|
||||||
|
- Use API-managed menus only for integrated stores (avoid manual Menu Maker edits to prevent drift).
|
||||||
|
|||||||
@ -230,7 +230,7 @@
|
|||||||
},
|
},
|
||||||
"/api/v1/uber/menu/upsert": {
|
"/api/v1/uber/menu/upsert": {
|
||||||
"post": {
|
"post": {
|
||||||
"summary": "Upsert store menu",
|
"summary": "Legacy upsert helper for store menu",
|
||||||
"tags": [
|
"tags": [
|
||||||
"Uber Menu"
|
"Uber Menu"
|
||||||
],
|
],
|
||||||
@ -254,6 +254,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/uber/menu/items": {
|
||||||
|
"post": {
|
||||||
|
"summary": "Update individual menu items (stock/price updates)",
|
||||||
|
"tags": [
|
||||||
|
"Uber Menu"
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Menu items updated"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/uber/menu": {
|
"/api/v1/uber/menu": {
|
||||||
"get": {
|
"get": {
|
||||||
"summary": "Fetch store menu",
|
"summary": "Fetch store menu",
|
||||||
|
|||||||
@ -199,6 +199,39 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Update Menu Items",
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "x-api-key",
|
||||||
|
"value": "{{apiKey}}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\n \"merchantId\": \"{{merchantId}}\",\n \"storeId\": \"{{storeId}}\",\n \"items\": [\n {\n \"id\": \"item_1\",\n \"price_info\": {\n \"price\": 799\n },\n \"suspension_info\": {\n \"suspend_until\": null\n }\n }\n ]\n}"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "{{baseUrl}}/api/v1/uber/menu/items",
|
||||||
|
"host": [
|
||||||
|
"{{baseUrl}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"v1",
|
||||||
|
"uber",
|
||||||
|
"menu",
|
||||||
|
"items"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "List Provisionable Stores",
|
"name": "List Provisionable Stores",
|
||||||
"request": {
|
"request": {
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
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: "/v1/eats/stores/{storeId}/menus",
|
||||||
|
itemsUpdate: "/v1/eats/stores/{storeId}/menus/items"
|
||||||
},
|
},
|
||||||
orders: {
|
orders: {
|
||||||
list: "/v1/eats/stores/{storeId}/orders",
|
list: "/v1/eats/stores/{storeId}/orders",
|
||||||
|
|||||||
@ -57,6 +57,21 @@ async function replaceMenu(req, res) {
|
|||||||
return res.json({ success: true, data });
|
return res.json({ success: true, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateMenuItems(req, res) {
|
||||||
|
const schema = z.object({
|
||||||
|
merchantId: z.string().min(1),
|
||||||
|
storeId: z.string().min(1),
|
||||||
|
items: z.array(z.any()).min(1)
|
||||||
|
});
|
||||||
|
const payload = schema.parse(req.body);
|
||||||
|
const data = await proxyService.updateMenuItems({
|
||||||
|
merchantId: payload.merchantId,
|
||||||
|
storeId: payload.storeId,
|
||||||
|
payload: { items: payload.items }
|
||||||
|
});
|
||||||
|
return res.json({ success: true, data });
|
||||||
|
}
|
||||||
|
|
||||||
async function listOrders(req, res) {
|
async function listOrders(req, res) {
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
merchantId: z.string().min(1),
|
merchantId: z.string().min(1),
|
||||||
@ -208,6 +223,7 @@ module.exports = {
|
|||||||
genericProxy,
|
genericProxy,
|
||||||
upsertMenu,
|
upsertMenu,
|
||||||
replaceMenu,
|
replaceMenu,
|
||||||
|
updateMenuItems,
|
||||||
getMenu,
|
getMenu,
|
||||||
listOrders,
|
listOrders,
|
||||||
orderAction,
|
orderAction,
|
||||||
|
|||||||
@ -151,6 +151,19 @@ async function menuGet({ merchantId, storeId }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateMenuItems({ merchantId, storeId, payload }) {
|
||||||
|
const uberPath = interpolatePath(uberEndpoints.menu.itemsUpdate, { storeId });
|
||||||
|
return callUberApi({
|
||||||
|
merchantId,
|
||||||
|
method: "POST",
|
||||||
|
uberPath,
|
||||||
|
body: payload,
|
||||||
|
wrapperRoute: "/api/v1/uber/menu/items",
|
||||||
|
authMode: "app",
|
||||||
|
scopes: AUTH_SCOPES.STORE
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function ordersList({ merchantId, storeId, query }) {
|
async function ordersList({ merchantId, storeId, query }) {
|
||||||
const uberPath = interpolatePath(uberEndpoints.orders.list, { storeId });
|
const uberPath = interpolatePath(uberEndpoints.orders.list, { storeId });
|
||||||
return callUberApi({
|
return callUberApi({
|
||||||
@ -326,6 +339,7 @@ module.exports = {
|
|||||||
menuUpsert,
|
menuUpsert,
|
||||||
menuReplace,
|
menuReplace,
|
||||||
menuGet,
|
menuGet,
|
||||||
|
updateMenuItems,
|
||||||
ordersList,
|
ordersList,
|
||||||
orderAction,
|
orderAction,
|
||||||
updateStoreHours,
|
updateStoreHours,
|
||||||
|
|||||||
@ -21,7 +21,7 @@ router.post("/uber/request", asyncHandler(controller.genericProxy));
|
|||||||
* @openapi
|
* @openapi
|
||||||
* /api/v1/uber/menu/upsert:
|
* /api/v1/uber/menu/upsert:
|
||||||
* post:
|
* post:
|
||||||
* summary: Upsert store menu
|
* summary: Legacy upsert helper for store menu
|
||||||
* tags:
|
* tags:
|
||||||
* - Uber Menu
|
* - Uber Menu
|
||||||
* responses:
|
* responses:
|
||||||
@ -43,6 +43,19 @@ router.post("/uber/menu/upsert", asyncHandler(controller.upsertMenu));
|
|||||||
*/
|
*/
|
||||||
router.put("/uber/menu/replace", asyncHandler(controller.replaceMenu));
|
router.put("/uber/menu/replace", asyncHandler(controller.replaceMenu));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /api/v1/uber/menu/items:
|
||||||
|
* post:
|
||||||
|
* summary: Update individual menu items (stock/price updates)
|
||||||
|
* tags:
|
||||||
|
* - Uber Menu
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Menu items updated
|
||||||
|
*/
|
||||||
|
router.post("/uber/menu/items", asyncHandler(controller.updateMenuItems));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @openapi
|
* @openapi
|
||||||
* /api/v1/uber/menu:
|
* /api/v1/uber/menu:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user