feat: expand order integration with order details, fulfillment resolution, and SLA metrics
This commit is contained in:
parent
8fb333918c
commit
6b768bfbf5
27
docs/developer-portal/06-order-integration-audit.md
Normal file
27
docs/developer-portal/06-order-integration-audit.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# 06 Order Integration Audit
|
||||||
|
|
||||||
|
Source checked: Uber Eats "Order Integration" section shared by you.
|
||||||
|
|
||||||
|
## Implemented Now
|
||||||
|
|
||||||
|
- Retrieve full order details after webhook:
|
||||||
|
- `GET /api/v1/uber/orders/{orderId}`
|
||||||
|
- Core order actions:
|
||||||
|
- accept / deny / ready / cancel
|
||||||
|
- Resolve fulfillment issues action:
|
||||||
|
- `POST /api/v1/uber/orders/{orderId}/action` with `action=resolve`
|
||||||
|
- SLA metric for 11.5 minute response window:
|
||||||
|
- `GET /api/v1/metrics/order-response-sla`
|
||||||
|
|
||||||
|
## Existing Before
|
||||||
|
|
||||||
|
- Order listing by store
|
||||||
|
- Webhook ingestion for order events
|
||||||
|
- Injection success metric
|
||||||
|
|
||||||
|
## Pending
|
||||||
|
|
||||||
|
- Confirmation of exact upstream path for fulfillment-issue resolution endpoint (verify against final API reference)
|
||||||
|
- Scheduled-order specific business rules and timers
|
||||||
|
- Courier handoff update endpoint coverage once reference section is shared
|
||||||
|
|
||||||
@ -5,6 +5,16 @@ Order flow for POS:
|
|||||||
- Receive order event
|
- Receive order event
|
||||||
- Fetch full order payload
|
- Fetch full order payload
|
||||||
- Accept/deny
|
- Accept/deny
|
||||||
|
- Resolve fulfillment issues when item(s) cannot be fulfilled
|
||||||
- Ready/handoff updates
|
- Ready/handoff updates
|
||||||
- Completion/cancellation reconciliation
|
- Completion/cancellation reconciliation
|
||||||
|
|
||||||
|
Typed routes:
|
||||||
|
|
||||||
|
- `GET /api/v1/uber/orders/{orderId}` (order details)
|
||||||
|
- `POST /api/v1/uber/orders/{orderId}/action` with action:
|
||||||
|
- `accept`
|
||||||
|
- `deny`
|
||||||
|
- `ready`
|
||||||
|
- `cancel`
|
||||||
|
- `resolve`
|
||||||
|
|||||||
@ -215,6 +215,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/metrics/order-response-sla": {
|
||||||
|
"get": {
|
||||||
|
"summary": "Get response SLA metric for order accept/deny timing",
|
||||||
|
"tags": [
|
||||||
|
"Metrics"
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "merchantId",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "windowDays",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "SLA metrics"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/uber/request": {
|
"/api/v1/uber/request": {
|
||||||
"post": {
|
"post": {
|
||||||
"summary": "Generic Uber passthrough for any Uber endpoint",
|
"summary": "Generic Uber passthrough for any Uber endpoint",
|
||||||
@ -293,6 +324,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/uber/orders/{orderId}": {
|
||||||
|
"get": {
|
||||||
|
"summary": "Retrieve full order details by order ID",
|
||||||
|
"tags": [
|
||||||
|
"Uber Orders"
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "orderId",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Order details retrieved"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/uber/stores": {
|
"/api/v1/uber/stores": {
|
||||||
"get": {
|
"get": {
|
||||||
"summary": "Retrieve all stores provisioned to developer account",
|
"summary": "Retrieve all stores provisioned to developer account",
|
||||||
@ -442,7 +496,7 @@
|
|||||||
},
|
},
|
||||||
"/api/v1/uber/orders/{orderId}/action": {
|
"/api/v1/uber/orders/{orderId}/action": {
|
||||||
"post": {
|
"post": {
|
||||||
"summary": "Trigger order action (accept, deny, ready, cancel)",
|
"summary": "Trigger order action (accept, deny, ready, cancel, resolve)",
|
||||||
"tags": [
|
"tags": [
|
||||||
"Uber Orders"
|
"Uber Orders"
|
||||||
],
|
],
|
||||||
|
|||||||
@ -398,6 +398,89 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Get Order Details",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "x-api-key",
|
||||||
|
"value": "{{apiKey}}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"url": {
|
||||||
|
"raw": "{{baseUrl}}/api/v1/uber/orders/{{orderId}}",
|
||||||
|
"host": [
|
||||||
|
"{{baseUrl}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"v1",
|
||||||
|
"uber",
|
||||||
|
"orders",
|
||||||
|
"{{orderId}}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Resolve Fulfillment Issue",
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "x-api-key",
|
||||||
|
"value": "{{apiKey}}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\n \"merchantId\": \"{{merchantId}}\",\n \"action\": \"resolve\",\n \"payload\": {\n \"issues\": []\n }\n}"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "{{baseUrl}}/api/v1/uber/orders/{{orderId}}/action",
|
||||||
|
"host": [
|
||||||
|
"{{baseUrl}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"v1",
|
||||||
|
"uber",
|
||||||
|
"orders",
|
||||||
|
"{{orderId}}",
|
||||||
|
"action"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Order Response SLA Metric",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "x-api-key",
|
||||||
|
"value": "{{apiKey}}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"url": {
|
||||||
|
"raw": "{{baseUrl}}/api/v1/metrics/order-response-sla",
|
||||||
|
"host": [
|
||||||
|
"{{baseUrl}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"v1",
|
||||||
|
"metrics",
|
||||||
|
"order-response-sla"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Set Holiday Hours",
|
"name": "Set Holiday Hours",
|
||||||
"request": {
|
"request": {
|
||||||
@ -615,6 +698,10 @@
|
|||||||
{
|
{
|
||||||
"key": "storeId",
|
"key": "storeId",
|
||||||
"value": ""
|
"value": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "orderId",
|
||||||
|
"value": ""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ module.exports = {
|
|||||||
orders: {
|
orders: {
|
||||||
list: "/v1/eats/stores/{storeId}/orders",
|
list: "/v1/eats/stores/{storeId}/orders",
|
||||||
getById: "/v1/eats/orders/{orderId}",
|
getById: "/v1/eats/orders/{orderId}",
|
||||||
|
resolveFulfillmentIssue: "/v1/eats/orders/{orderId}/resolve_fulfillment_issues",
|
||||||
accept: "/v1/eats/orders/{orderId}/accept_pos_order",
|
accept: "/v1/eats/orders/{orderId}/accept_pos_order",
|
||||||
deny: "/v1/eats/orders/{orderId}/deny_pos_order",
|
deny: "/v1/eats/orders/{orderId}/deny_pos_order",
|
||||||
readyForPickup: "/v1/eats/orders/{orderId}/pos_order_ready_for_pickup",
|
readyForPickup: "/v1/eats/orders/{orderId}/pos_order_ready_for_pickup",
|
||||||
|
|||||||
@ -5,6 +5,14 @@ function nowIso() {
|
|||||||
return new Date().toISOString();
|
return new Date().toISOString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractOrderIdFromPath(uberPath) {
|
||||||
|
if (!uberPath) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const match = uberPath.match(/\/orders\/([^/]+)/i);
|
||||||
|
return match ? decodeURIComponent(match[1]) : null;
|
||||||
|
}
|
||||||
|
|
||||||
const merchantRepository = {
|
const merchantRepository = {
|
||||||
upsert({ id, name, externalRef }) {
|
upsert({ id, name, externalRef }) {
|
||||||
const timestamp = nowIso();
|
const timestamp = nowIso();
|
||||||
@ -163,9 +171,11 @@ const webhookRepository = {
|
|||||||
|
|
||||||
const apiLogRepository = {
|
const apiLogRepository = {
|
||||||
insert({ merchantId, method, wrapperRoute, uberPath, responseStatus, requestBody, responseBody }) {
|
insert({ merchantId, method, wrapperRoute, uberPath, responseStatus, requestBody, responseBody }) {
|
||||||
|
const orderId = extractOrderIdFromPath(uberPath);
|
||||||
const row = {
|
const row = {
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
merchant_id: merchantId || null,
|
merchant_id: merchantId || null,
|
||||||
|
order_id: orderId,
|
||||||
http_method: method,
|
http_method: method,
|
||||||
wrapper_route: wrapperRoute,
|
wrapper_route: wrapperRoute,
|
||||||
uber_path: uberPath,
|
uber_path: uberPath,
|
||||||
@ -177,11 +187,11 @@ const apiLogRepository = {
|
|||||||
|
|
||||||
const stmt = db.prepare(`
|
const stmt = db.prepare(`
|
||||||
INSERT INTO api_logs (
|
INSERT INTO api_logs (
|
||||||
id, merchant_id, http_method, wrapper_route, uber_path,
|
id, merchant_id, order_id, http_method, wrapper_route, uber_path,
|
||||||
response_status, request_json, response_json, created_at
|
response_status, request_json, response_json, created_at
|
||||||
)
|
)
|
||||||
VALUES (
|
VALUES (
|
||||||
@id, @merchant_id, @http_method, @wrapper_route, @uber_path,
|
@id, @merchant_id, @order_id, @http_method, @wrapper_route, @uber_path,
|
||||||
@response_status, @request_json, @response_json, @created_at
|
@response_status, @request_json, @response_json, @created_at
|
||||||
)
|
)
|
||||||
`);
|
`);
|
||||||
@ -227,6 +237,91 @@ const apiLogRepository = {
|
|||||||
failed: total - success,
|
failed: total - success,
|
||||||
successRate
|
successRate
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getOrderResponseSlaStats({ merchantId, sinceIso }) {
|
||||||
|
const webhookFilters = [
|
||||||
|
"provider = 'uber'",
|
||||||
|
"(event_type = 'orders.notification' OR event_type = 'orders.scheduled.notification')",
|
||||||
|
"resource_id IS NOT NULL"
|
||||||
|
];
|
||||||
|
const webhookParams = [];
|
||||||
|
|
||||||
|
if (merchantId) {
|
||||||
|
webhookFilters.push("merchant_id = ?");
|
||||||
|
webhookParams.push(merchantId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sinceIso) {
|
||||||
|
webhookFilters.push("received_at >= ?");
|
||||||
|
webhookParams.push(sinceIso);
|
||||||
|
}
|
||||||
|
|
||||||
|
const webhookRows = db
|
||||||
|
.prepare(
|
||||||
|
`
|
||||||
|
SELECT resource_id, merchant_id, received_at
|
||||||
|
FROM webhook_events
|
||||||
|
WHERE ${webhookFilters.join(" AND ")}
|
||||||
|
ORDER BY received_at ASC
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.all(...webhookParams);
|
||||||
|
|
||||||
|
const uniqueOrders = new Map();
|
||||||
|
webhookRows.forEach((row) => {
|
||||||
|
if (!uniqueOrders.has(row.resource_id)) {
|
||||||
|
uniqueOrders.set(row.resource_id, row);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let withinSla = 0;
|
||||||
|
let responded = 0;
|
||||||
|
|
||||||
|
uniqueOrders.forEach((row, orderId) => {
|
||||||
|
const action = db
|
||||||
|
.prepare(
|
||||||
|
`
|
||||||
|
SELECT created_at
|
||||||
|
FROM api_logs
|
||||||
|
WHERE order_id = ?
|
||||||
|
AND (
|
||||||
|
uber_path LIKE '%/accept_pos_order'
|
||||||
|
OR uber_path LIKE '%/deny_pos_order'
|
||||||
|
)
|
||||||
|
AND response_status >= 200
|
||||||
|
AND response_status < 300
|
||||||
|
ORDER BY created_at ASC
|
||||||
|
LIMIT 1
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.get(orderId);
|
||||||
|
|
||||||
|
if (!action) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
responded += 1;
|
||||||
|
const receivedAtMs = new Date(row.received_at).getTime();
|
||||||
|
const actionAtMs = new Date(action.created_at).getTime();
|
||||||
|
const elapsedMs = actionAtMs - receivedAtMs;
|
||||||
|
if (!Number.isNaN(elapsedMs) && elapsedMs <= 11.5 * 60 * 1000) {
|
||||||
|
withinSla += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalNotifications = uniqueOrders.size;
|
||||||
|
const withinSlaRate =
|
||||||
|
totalNotifications === 0 ? 0 : Number(((withinSla / totalNotifications) * 100).toFixed(2));
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalNotifications,
|
||||||
|
responded,
|
||||||
|
noResponseYet: totalNotifications - responded,
|
||||||
|
withinSla,
|
||||||
|
breachedSla: responded - withinSla,
|
||||||
|
withinSlaRate
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -65,6 +65,7 @@ function initSchema() {
|
|||||||
CREATE TABLE IF NOT EXISTS api_logs (
|
CREATE TABLE IF NOT EXISTS api_logs (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
merchant_id TEXT,
|
merchant_id TEXT,
|
||||||
|
order_id TEXT,
|
||||||
http_method TEXT NOT NULL,
|
http_method TEXT NOT NULL,
|
||||||
wrapper_route TEXT NOT NULL,
|
wrapper_route TEXT NOT NULL,
|
||||||
uber_path TEXT NOT NULL,
|
uber_path TEXT NOT NULL,
|
||||||
@ -115,6 +116,10 @@ function initSchema() {
|
|||||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_webhook_events_dedupe_key
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_webhook_events_dedupe_key
|
||||||
ON webhook_events(dedupe_key);
|
ON webhook_events(dedupe_key);
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
if (!tableHasColumn("api_logs", "order_id")) {
|
||||||
|
db.exec("ALTER TABLE api_logs ADD COLUMN order_id TEXT");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@ -38,7 +38,32 @@ async function getInjectionSuccess(req, res) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
async function getOrderResponseSla(req, res) {
|
||||||
getInjectionSuccess
|
const schema = z.object({
|
||||||
};
|
merchantId: z.string().optional(),
|
||||||
|
windowDays: z.string().optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
const query = schema.parse(req.query);
|
||||||
|
const sinceIso = toSinceIso(query.windowDays);
|
||||||
|
const stats = apiLogRepository.getOrderResponseSlaStats({
|
||||||
|
merchantId: query.merchantId,
|
||||||
|
sinceIso
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
metric: "order_response_within_11_5_minutes",
|
||||||
|
merchantId: query.merchantId || "all",
|
||||||
|
windowDays: query.windowDays ? Number(query.windowDays) : "all_time",
|
||||||
|
targetMinutes: 11.5,
|
||||||
|
...stats
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getInjectionSuccess,
|
||||||
|
getOrderResponseSla
|
||||||
|
};
|
||||||
|
|||||||
@ -86,10 +86,17 @@ async function listOrders(req, res) {
|
|||||||
return res.json({ success: true, data });
|
return res.json({ success: true, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getOrderById(req, res) {
|
||||||
|
const data = await proxyService.getOrderById({
|
||||||
|
orderId: req.params.orderId
|
||||||
|
});
|
||||||
|
return res.json({ success: true, data });
|
||||||
|
}
|
||||||
|
|
||||||
async function orderAction(req, res) {
|
async function orderAction(req, res) {
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
merchantId: z.string().min(1),
|
merchantId: z.string().min(1),
|
||||||
action: z.enum(["accept", "deny", "ready", "cancel"]),
|
action: z.enum(["accept", "deny", "ready", "cancel", "resolve"]),
|
||||||
payload: z.any().optional()
|
payload: z.any().optional()
|
||||||
});
|
});
|
||||||
const parsed = schema.parse(req.body);
|
const parsed = schema.parse(req.body);
|
||||||
@ -226,6 +233,7 @@ module.exports = {
|
|||||||
updateMenuItems,
|
updateMenuItems,
|
||||||
getMenu,
|
getMenu,
|
||||||
listOrders,
|
listOrders,
|
||||||
|
getOrderById,
|
||||||
orderAction,
|
orderAction,
|
||||||
updateHours,
|
updateHours,
|
||||||
listProvisionableStores,
|
listProvisionableStores,
|
||||||
|
|||||||
@ -177,8 +177,20 @@ async function ordersList({ merchantId, storeId, query }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getOrderById({ orderId }) {
|
||||||
|
const uberPath = interpolatePath(uberEndpoints.orders.getById, { orderId });
|
||||||
|
return callUberApi({
|
||||||
|
method: "GET",
|
||||||
|
uberPath,
|
||||||
|
wrapperRoute: "/api/v1/uber/orders/:orderId",
|
||||||
|
authMode: "app",
|
||||||
|
scopes: AUTH_SCOPES.ORDER
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function orderAction({ merchantId, orderId, action, payload }) {
|
async function orderAction({ merchantId, orderId, action, payload }) {
|
||||||
const routeMap = {
|
const routeMap = {
|
||||||
|
resolve: uberEndpoints.orders.resolveFulfillmentIssue,
|
||||||
accept: uberEndpoints.orders.accept,
|
accept: uberEndpoints.orders.accept,
|
||||||
deny: uberEndpoints.orders.deny,
|
deny: uberEndpoints.orders.deny,
|
||||||
ready: uberEndpoints.orders.readyForPickup,
|
ready: uberEndpoints.orders.readyForPickup,
|
||||||
@ -341,6 +353,7 @@ module.exports = {
|
|||||||
menuGet,
|
menuGet,
|
||||||
updateMenuItems,
|
updateMenuItems,
|
||||||
ordersList,
|
ordersList,
|
||||||
|
getOrderById,
|
||||||
orderAction,
|
orderAction,
|
||||||
updateStoreHours,
|
updateStoreHours,
|
||||||
listProvisionableStores,
|
listProvisionableStores,
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
const express = require("express");
|
const express = require("express");
|
||||||
const asyncHandler = require("../middleware/asyncHandler");
|
const asyncHandler = require("../middleware/asyncHandler");
|
||||||
const { getInjectionSuccess } = require("../modules/metrics/metrics.controller");
|
const {
|
||||||
|
getInjectionSuccess,
|
||||||
|
getOrderResponseSla
|
||||||
|
} = require("../modules/metrics/metrics.controller");
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@ -28,5 +31,28 @@ const router = express.Router();
|
|||||||
*/
|
*/
|
||||||
router.get("/metrics/injection-success", asyncHandler(getInjectionSuccess));
|
router.get("/metrics/injection-success", asyncHandler(getInjectionSuccess));
|
||||||
|
|
||||||
module.exports = router;
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /api/v1/metrics/order-response-sla:
|
||||||
|
* get:
|
||||||
|
* summary: Get response SLA metric for order accept/deny timing
|
||||||
|
* tags:
|
||||||
|
* - Metrics
|
||||||
|
* parameters:
|
||||||
|
* - in: query
|
||||||
|
* name: merchantId
|
||||||
|
* required: false
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* - in: query
|
||||||
|
* name: windowDays
|
||||||
|
* required: false
|
||||||
|
* schema:
|
||||||
|
* type: integer
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: SLA metrics
|
||||||
|
*/
|
||||||
|
router.get("/metrics/order-response-sla", asyncHandler(getOrderResponseSla));
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
|||||||
@ -82,6 +82,25 @@ router.get("/uber/menu", asyncHandler(controller.getMenu));
|
|||||||
*/
|
*/
|
||||||
router.get("/uber/orders", asyncHandler(controller.listOrders));
|
router.get("/uber/orders", asyncHandler(controller.listOrders));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /api/v1/uber/orders/{orderId}:
|
||||||
|
* get:
|
||||||
|
* summary: Retrieve full order details by order ID
|
||||||
|
* tags:
|
||||||
|
* - Uber Orders
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: orderId
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Order details retrieved
|
||||||
|
*/
|
||||||
|
router.get("/uber/orders/:orderId", asyncHandler(controller.getOrderById));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @openapi
|
* @openapi
|
||||||
* /api/v1/uber/stores:
|
* /api/v1/uber/stores:
|
||||||
@ -203,7 +222,7 @@ router.post("/uber/stores/:storeId/holiday-hours", asyncHandler(controller.setHo
|
|||||||
* @openapi
|
* @openapi
|
||||||
* /api/v1/uber/orders/{orderId}/action:
|
* /api/v1/uber/orders/{orderId}/action:
|
||||||
* post:
|
* post:
|
||||||
* summary: Trigger order action (accept, deny, ready, cancel)
|
* summary: Trigger order action (accept, deny, ready, cancel, resolve)
|
||||||
* tags:
|
* tags:
|
||||||
* - Uber Orders
|
* - Uber Orders
|
||||||
* parameters:
|
* parameters:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user