diff --git a/docs/developer-portal/11-going-live-audit.md b/docs/developer-portal/11-going-live-audit.md new file mode 100644 index 0000000..8f31fc2 --- /dev/null +++ b/docs/developer-portal/11-going-live-audit.md @@ -0,0 +1,26 @@ +# 11 Going Live Audit + +Source checked: Uber Eats "Going Live" section shared by you. + +## Implemented Now + +- Go-live readiness metric endpoint for pilot gating: + - `GET /api/v1/metrics/go-live-readiness` + - computes 3-day injection success and evaluates against configurable threshold (default 98%) +- Existing production readiness controls already present: + - webhook security (signature + optional basic auth) + - OAuth domain pairing checks + - injection success monitoring endpoint + +## Existing Before + +- Menu upload and POS integration activation APIs +- Provisioning and de-provisioning flow endpoints +- Webhook ingestion and order action APIs + +## Pending (Operational / Process) + +- Scheduling formal joint verification run with Uber support team +- Production app creation and scope whitelisting workflow in dashboard +- Pilot launch operational communication workflow outside API service + diff --git a/docs/developer-portal/11-production-go-live.md b/docs/developer-portal/11-production-go-live.md index a5963ea..d264a7a 100644 --- a/docs/developer-portal/11-production-go-live.md +++ b/docs/developer-portal/11-production-go-live.md @@ -7,3 +7,13 @@ Go-live readiness: - Alerting and log monitoring enabled - Token refresh and failure runbooks ready - Injection success rate monitoring enabled (`target >= 99%`) +- Pilot launch metric check enabled (`>= 98%` over 3 days): + - `GET /api/v1/metrics/go-live-readiness` + +Pilot launch workflow: + +1. Provision pilot store to production app. +2. Upload menu (`PUT /api/v1/uber/menu/replace`). +3. Enable POS integration (`PATCH /api/v1/uber/stores/{storeId}/pos-data` with `integration_enabled=true`). +4. Monitor injection success and order response SLA. +5. Continue launches after pilot stability target is met. diff --git a/docs/openapi/openapi.json b/docs/openapi/openapi.json index 1918aac..e366405 100644 --- a/docs/openapi/openapi.json +++ b/docs/openapi/openapi.json @@ -246,6 +246,37 @@ } } }, + "/api/v1/metrics/go-live-readiness": { + "get": { + "summary": "Check pilot go-live readiness based on 3-day injection success threshold", + "tags": [ + "Metrics" + ], + "parameters": [ + { + "in": "query", + "name": "merchantId", + "required": false, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "thresholdPercent", + "required": false, + "schema": { + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "Go-live readiness status" + } + } + } + }, "/api/v1/uber/request": { "post": { "summary": "Generic Uber passthrough for any Uber endpoint", diff --git a/postman/Uber_Wrapper.postman_collection.json b/postman/Uber_Wrapper.postman_collection.json index 9b04d81..afff804 100644 --- a/postman/Uber_Wrapper.postman_collection.json +++ b/postman/Uber_Wrapper.postman_collection.json @@ -549,6 +549,36 @@ } } }, + { + "name": "Go Live Readiness Metric", + "request": { + "method": "GET", + "header": [ + { + "key": "x-api-key", + "value": "{{apiKey}}" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v1/metrics/go-live-readiness?merchantId={{merchantId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "metrics", + "go-live-readiness" + ], + "query": [ + { + "key": "merchantId", + "value": "{{merchantId}}" + } + ] + } + } + }, { "name": "Fetch Reporting CSV", "request": { diff --git a/src/modules/metrics/metrics.controller.js b/src/modules/metrics/metrics.controller.js index f7b40fb..01312a5 100644 --- a/src/modules/metrics/metrics.controller.js +++ b/src/modules/metrics/metrics.controller.js @@ -63,7 +63,38 @@ async function getOrderResponseSla(req, res) { }); } +async function getGoLiveReadiness(req, res) { + const schema = z.object({ + merchantId: z.string().optional(), + thresholdPercent: z.coerce.number().optional() + }); + + const query = schema.parse(req.query); + const sinceIso = toSinceIso("3"); + const stats = apiLogRepository.getInjectionSuccessStats({ + merchantId: query.merchantId, + sinceIso + }); + const threshold = query.thresholdPercent ?? 98; + const hasData = stats.total > 0; + const eligible = hasData && stats.successRate >= threshold; + + return res.json({ + success: true, + data: { + metric: "go_live_readiness", + merchantId: query.merchantId || "all", + pilotWindowDays: 3, + requiredInjectionSuccessPercent: threshold, + hasData, + eligible, + ...stats + } + }); +} + module.exports = { getInjectionSuccess, - getOrderResponseSla + getOrderResponseSla, + getGoLiveReadiness }; diff --git a/src/routes/metrics.routes.js b/src/routes/metrics.routes.js index 5ff2ada..23fc22f 100644 --- a/src/routes/metrics.routes.js +++ b/src/routes/metrics.routes.js @@ -2,7 +2,8 @@ const express = require("express"); const asyncHandler = require("../middleware/asyncHandler"); const { getInjectionSuccess, - getOrderResponseSla + getOrderResponseSla, + getGoLiveReadiness } = require("../modules/metrics/metrics.controller"); const router = express.Router(); @@ -55,4 +56,28 @@ router.get("/metrics/injection-success", asyncHandler(getInjectionSuccess)); */ router.get("/metrics/order-response-sla", asyncHandler(getOrderResponseSla)); +/** + * @openapi + * /api/v1/metrics/go-live-readiness: + * get: + * summary: Check pilot go-live readiness based on 3-day injection success threshold + * tags: + * - Metrics + * parameters: + * - in: query + * name: merchantId + * required: false + * schema: + * type: string + * - in: query + * name: thresholdPercent + * required: false + * schema: + * type: number + * responses: + * 200: + * description: Go-live readiness status + */ +router.get("/metrics/go-live-readiness", asyncHandler(getGoLiveReadiness)); + module.exports = router;