UBER-EATS-Wrapper/src/modules/proxy/proxy.controller.js

760 lines
21 KiB
JavaScript

const { z } = require("zod");
const proxyService = require("./proxy.service");
const genericSchema = z.object({
merchantId: z.string().min(1).optional(),
method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("GET"),
path: z.string().min(1),
authMode: z.enum(["app", "merchant"]).optional(),
scopes: z.string().optional(),
query: z.record(z.string(), z.any()).optional(),
body: z.any().optional()
});
async function genericProxy(req, res) {
const payload = genericSchema.parse(req.body);
const data = await proxyService.genericProxy(payload);
return res.json({ success: true, data });
}
async function upsertMenu(req, res) {
const schema = z.object({
merchantId: z.string().min(1),
storeId: z.string().min(1),
menu: z.any()
});
const payload = schema.parse(req.body);
const data = await proxyService.menuUpsert({
merchantId: payload.merchantId,
storeId: payload.storeId,
payload: payload.menu
});
return res.json({ success: true, data });
}
async function getMenu(req, res) {
const schema = z.object({
merchantId: 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({
merchantId: payload.merchantId,
storeId: payload.storeId,
menuType: payload.menu_type || payload.menuType
});
return res.json({ success: true, data });
}
async function replaceMenu(req, res) {
const schema = z.object({
merchantId: z.string().min(1),
storeId: z.string().min(1),
menu: z.any()
});
const payload = schema.parse(req.body);
const data = await proxyService.menuReplace({
merchantId: payload.merchantId,
storeId: payload.storeId,
payload: payload.menu
});
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) {
const schema = z.object({
merchantId: z.string().min(1),
storeId: z.string().min(1)
});
const payload = schema.parse(req.query);
const data = await proxyService.ordersList({
merchantId: payload.merchantId,
storeId: payload.storeId,
query: req.query
});
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) {
const schema = z.object({
merchantId: z.string().min(1),
action: z.enum(["accept", "deny", "ready", "cancel", "resolve"]),
payload: z.any().optional()
});
const parsed = schema.parse(req.body);
const data = await proxyService.orderAction({
merchantId: parsed.merchantId,
orderId: req.params.orderId,
action: parsed.action,
payload: parsed.payload
});
return res.json({ success: true, data });
}
async function resolveFulfillmentIssues(req, res) {
const quantityObject = z
.object({
in_sellable_unit: z.any().optional(),
in_priceable_unit: z.any().optional()
})
.optional();
const fulfillmentIssueSchema = z.object({
cart_item_id: z.string().optional(),
issue_type: z.enum([
"FOUND_ITEM",
"PARTIAL_AVAILABILITY",
"OUT_OF_ITEM"
]),
action_type: z
.enum([
"REPLACE_FOR_ME",
"SUBSTITUTE_ME",
"REMOVE_ITEM",
"ALTERNATIVE_ITEM",
"SUBSTITUTION_REJECTED"
])
.optional(),
item_substitute: z.any().optional(),
item_availability: z
.object({
items_available: quantityObject
})
.optional()
});
const schema = z.object({
issues: z.array(fulfillmentIssueSchema).min(1)
});
const payload = schema.parse(req.body || {});
const data = await proxyService.resolveFulfillmentIssues({
orderId: req.params.orderId,
payload
});
return res.json({ success: true, data });
}
async function markOrderReady(req, res) {
const data = await proxyService.markOrderReady({
orderId: req.params.orderId,
payload: req.body || {}
});
return res.json({ success: true, data });
}
async function updateHours(req, res) {
const schema = z.object({
merchantId: z.string().min(1),
storeId: z.string().min(1),
hours: z.any()
});
const payload = schema.parse(req.body);
const data = await proxyService.updateStoreHours({
merchantId: payload.merchantId,
storeId: payload.storeId,
payload: payload.hours
});
return res.json({ success: true, data });
}
async function listProvisionableStores(req, res) {
const schema = z.object({
merchantId: z.string().min(1)
});
const payload = schema.parse(req.query);
const data = await proxyService.listProvisionableStores({
merchantId: payload.merchantId,
query: req.query
});
return res.json({ success: true, data });
}
async function listStores(req, res) {
const data = await proxyService.listStores({
query: req.query
});
return res.json({ success: true, data });
}
async function getStoreById(req, res) {
const data = await proxyService.getStoreById({
storeId: req.params.storeId
});
return res.json({ success: true, data });
}
async function getStoreStatus(req, res) {
const data = await proxyService.getStoreStatus({
storeId: req.params.storeId
});
return res.json({ success: true, data });
}
async function setStoreStatus(req, res) {
const schema = z.object({
status: z.string().optional(),
reason: z.string().optional()
});
const payload = schema.partial().parse(req.body || {});
const data = await proxyService.setStoreStatus({
storeId: req.params.storeId,
payload
});
return res.json({ success: true, data });
}
async function getHolidayHours(req, res) {
const data = await proxyService.getHolidayHours({
storeId: req.params.storeId
});
return res.json({ success: true, data });
}
async function setHolidayHours(req, res) {
const data = await proxyService.setHolidayHours({
storeId: req.params.storeId,
payload: req.body || {}
});
return res.json({ success: true, data });
}
async function createPosData(req, res) {
const allowedCustomerRequestsSchema = z
.object({
allow_single_use_items_requests: z.boolean().optional(),
allow_special_instruction_requests: z.boolean().optional()
})
.optional();
const webhooksConfigSchema = z
.object({
order_release_webhooks: z.record(z.string(), z.any()).optional(),
schedule_order_webhooks: z.record(z.string(), z.any()).optional(),
delivery_status_webhooks: z.record(z.string(), z.any()).optional(),
webhooks_version: z.string().optional()
})
.optional();
const posDataSchema = z.object({
allowed_customer_requests: allowedCustomerRequestsSchema,
integrator_brand_id: z.string().optional(),
integrator_store_id: z.string().optional(),
is_order_manager: z.boolean().optional(),
merchant_store_id: z.string().optional(),
require_manual_acceptance: z.boolean().optional(),
store_configuration_data: z.string().optional(),
webhooks_config: webhooksConfigSchema,
integration_enabled: z.boolean().optional()
});
const schema = z.object({
merchantId: z.string().min(1),
posData: posDataSchema
});
const payload = schema.parse(req.body);
const data = await proxyService.createPosData({
merchantId: payload.merchantId,
storeId: req.params.storeId,
payload: payload.posData
});
return res.json({ success: true, data });
}
async function getPosData(req, res) {
const data = await proxyService.getPosData({
storeId: req.params.storeId
});
return res.json({ success: true, data });
}
async function patchPosData(req, res) {
const allowedCustomerRequestsSchema = z
.object({
allow_single_use_items_requests: z.boolean().optional(),
allow_special_instruction_requests: z.boolean().optional()
})
.optional();
const webhooksConfigSchema = z
.object({
order_release_webhooks: z.record(z.string(), z.any()).optional(),
schedule_order_webhooks: z.record(z.string(), z.any()).optional(),
delivery_status_webhooks: z.record(z.string(), z.any()).optional(),
webhooks_version: z.string().optional()
})
.optional();
const posDataSchema = z
.object({
allowed_customer_requests: allowedCustomerRequestsSchema,
integrator_brand_id: z.string().optional(),
integrator_store_id: z.string().optional(),
is_order_manager: z.boolean().optional(),
merchant_store_id: z.string().optional(),
require_manual_acceptance: z.boolean().optional(),
store_configuration_data: z.string().optional(),
webhooks_config: webhooksConfigSchema,
integration_enabled: z.boolean().optional()
})
.refine((value) => Object.keys(value).length > 0, {
message: "posData must include at least one updatable field"
});
const schema = z.object({
posData: posDataSchema
});
const payload = schema.parse(req.body);
const data = await proxyService.patchPosData({
storeId: req.params.storeId,
payload: payload.posData
});
return res.json({ success: true, data });
}
async function deletePosData(req, res) {
const data = await proxyService.deletePosData({
storeId: req.params.storeId
});
return res.json({ success: true, data });
}
async function deliveryListStores(req, res) {
const schema = z.object({
next_page_token: z.string().optional(),
page_size: z.coerce.number().int().min(1).max(200).optional()
});
const query = schema.parse(req.query || {});
const data = await proxyService.deliveryListStores({ query });
return res.json({ success: true, data });
}
async function deliveryGetStoreDetails(req, res) {
const schema = z.object({
expand: z.string().optional()
});
const query = schema.parse(req.query || {});
const data = await proxyService.deliveryGetStoreDetails({
storeId: req.params.storeId,
query
});
return res.json({ success: true, data });
}
async function deliveryUpdateStore(req, res) {
const contactSchema = z
.object({
email: z.string().optional(),
name: z.string().optional(),
phone_number: z.string().optional()
})
.optional();
const locationSchema = z
.object({
latitude: z.number().optional(),
longitude: z.number().optional(),
street_address_line_one: z.string().optional(),
street_address_line_two: z.string().optional(),
city: z.string().optional(),
country: z.string().optional(),
postal_code: z.union([z.string(), z.number()]).optional(),
unit_number: z.union([z.string(), z.number()]).optional(),
business_name: z.string().optional()
})
.optional();
const schema = z.object({
contact: contactSchema,
location: locationSchema,
pickup_instructions: z.string().optional()
});
const payload = schema
.refine((value) => Object.keys(value).length > 0, {
message: "At least one of contact/location/pickup_instructions is required"
})
.parse(req.body || {});
const data = await proxyService.deliveryUpdateStore({
storeId: req.params.storeId,
payload
});
return res.json({ success: true, data });
}
async function deliveryGetStoreStatus(req, res) {
const data = await proxyService.deliveryGetStoreStatus({
storeId: req.params.storeId
});
return res.json({ success: true, data });
}
async function deliverySetStoreStatus(req, res) {
const schema = z.object({
status: z.enum(["ONLINE", "OFFLINE"]),
is_offline_until: z.string().optional(),
reason: z.string().optional()
});
const payload = schema.parse(req.body || {});
const data = await proxyService.deliverySetStoreStatus({
storeId: req.params.storeId,
payload
});
return res.json({ success: true, data });
}
async function deliveryUpdatePrepTime(req, res) {
const schema = z.object({
default_prep_time: z.coerce.number().int().min(0).max(10800)
});
const payload = schema.parse(req.body || {});
const data = await proxyService.deliveryUpdatePrepTime({
storeId: req.params.storeId,
payload
});
return res.json({ success: true, data });
}
async function deliveryUpdateFulfillmentConfig(req, res) {
const schema = z.object({
override_config: z.record(z.string(), z.any())
});
const payload = schema.parse(req.body || {});
const data = await proxyService.deliveryUpdateFulfillmentConfig({
storeId: req.params.storeId,
payload
});
return res.json({ success: true, data });
}
async function deliveryGetOrderDetails(req, res) {
const schema = z.object({
expand: z.string().optional()
});
const query = schema.parse(req.query || {});
const data = await proxyService.deliveryGetOrderDetails({
orderId: req.params.orderId,
query
});
return res.json({ success: true, data });
}
async function deliveryListOrders(req, res) {
const schema = z.object({
expand: z.string().optional(),
state: z.string().optional(),
status: z.string().optional(),
start_time: z.string().optional(),
end_time: z.string().optional(),
next_page_token: z.string().optional(),
page_size: z.coerce.number().int().min(1).max(50).optional()
});
const query = schema.parse(req.query || {});
const data = await proxyService.deliveryListOrders({
storeId: req.params.storeId,
query
});
return res.json({ success: true, data });
}
async function deliveryAcceptOrder(req, res) {
const schema = z.object({
ready_for_pickup_time: z.string().optional(),
external_reference_id: z.string().optional(),
accepted_by: z.string().optional(),
order_pickup_instructions: z.string().optional()
});
const payload = schema.parse(req.body || {});
const data = await proxyService.deliveryAcceptOrder({
orderId: req.params.orderId,
payload
});
return res.json({ success: true, data });
}
async function deliveryDenyOrder(req, res) {
const denyReasonSchema = z.object({
info: z.string().optional(),
type: z.string().min(1),
client_error_code: z.string().optional(),
item_metadata: z.record(z.string(), z.any()).optional()
});
const schema = z.object({
deny_reason: denyReasonSchema
});
const payload = schema.parse(req.body || {});
const data = await proxyService.deliveryDenyOrder({
orderId: req.params.orderId,
payload
});
return res.json({ success: true, data });
}
async function deliveryCancelOrder(req, res) {
const cancelReasonSchema = z.object({
info: z.string().optional(),
type: z.string().min(1),
client_error_code: z.string().optional(),
item_metadata: z.record(z.string(), z.any()).optional()
});
const schema = z.object({
cancellation_reason: cancelReasonSchema
});
const payload = schema.parse(req.body || {});
const data = await proxyService.deliveryCancelOrder({
orderId: req.params.orderId,
payload
});
return res.json({ success: true, data });
}
async function deliveryMarkOrderReady(req, res) {
const data = await proxyService.deliveryMarkOrderReady({
orderId: req.params.orderId,
payload: req.body || {}
});
return res.json({ success: true, data });
}
async function deliveryAdjustOrderPrice(req, res) {
const schema = z
.object({
amount_e5: z.coerce.number(),
tax_rate: z.string().optional(),
reason: z.enum([
"REQUESTED_ADD_ONS",
"BIGGER_SIZE",
"NEW_ITEM_ADDED",
"ITEM_SOLD_OUT",
"REMOVED_ITEM",
"ADD_ON_UNAVAILABLE",
"OTHER"
]),
custom_reason: z.string().optional()
})
.refine((value) => value.reason !== "OTHER" || Boolean(value.custom_reason), {
message: "custom_reason is required when reason is OTHER"
});
const payload = schema.parse(req.body || {});
const data = await proxyService.deliveryAdjustOrderPrice({
orderId: req.params.orderId,
payload
});
return res.json({ success: true, data });
}
async function deliveryUpdateReadyTime(req, res) {
const schema = z.object({
ready_for_pickup_time: z.string().min(1)
});
const payload = schema.parse(req.body || {});
const data = await proxyService.deliveryUpdateReadyTime({
orderId: req.params.orderId,
payload
});
return res.json({ success: true, data });
}
async function deliveryResolveFulfillmentIssues(req, res) {
const schema = z.object({
fulfillment_issues: z.array(z.any()).min(1)
});
const payload = schema.parse(req.body || {});
const data = await proxyService.deliveryResolveFulfillmentIssues({
orderId: req.params.orderId,
payload
});
return res.json({ success: true, data });
}
async function deliveryGetReplacementRecommendations(req, res) {
const schema = z.object({
id: z.string().min(1),
order_id: z.string().min(1),
store_id: z.string().min(1)
});
const payload = schema.parse(req.body || {});
const data = await proxyService.deliveryGetReplacementRecommendations({
payload
});
return res.json({ success: true, data });
}
async function deliveryUpdatePartnerCount(req, res) {
const schema = z.object({
delivery_partner_count: z.coerce.number().int().min(2).max(5)
});
const payload = schema.parse(req.body || {});
const data = await proxyService.deliveryUpdatePartnerCount({
orderId: req.params.orderId,
payload
});
return res.json({ success: true, data });
}
async function deliveryByocIngestCourierLocation(req, res) {
const locationEventSchema = z.object({
timestamp: z.union([z.number(), z.string()]).optional(),
latitude: z.number().optional(),
longitude: z.number().optional(),
accuracy_meters: z.number().optional(),
speed_mps: z.number().optional(),
heading_degrees: z.number().optional()
});
const schema = z.object({
location_request: z.object({
order_workflow_uuid: z.string().uuid(),
restaurant_uuid: z.string().uuid(),
is_batched_order: z.boolean().optional(),
location_events: z.array(locationEventSchema)
})
});
const payload = schema.parse(req.body || {});
const data = await proxyService.deliveryByocIngestCourierLocation({
payload
});
return res.json({ success: true, data });
}
async function deliveryCreatePromotion(req, res) {
const budgetSchema = z
.object({
unlimited_budget: z.boolean().optional(),
amount_e5: z.coerce.number().optional()
})
.optional();
const promotionSchema = z.object({
start_time: z.string(),
end_time: z.string(),
external_promotion_id: z.string().optional(),
user_group: z.enum(["ALL_CUSTOMERS", "FIRST_TIME_CUSTOMERS"]).optional(),
allow_unlimited_apply: z.boolean().optional(),
currency_code: z.string().optional(),
budget: budgetSchema,
promo_type: z.string(),
promotion_discount: z.record(z.string(), z.any()).optional(),
promotion_customization: z.record(z.string(), z.any()).optional()
});
const payload = promotionSchema.parse(req.body || {});
const data = await proxyService.deliveryCreatePromotion({
storeId: req.params.storeId,
payload
});
return res.json({ success: true, data });
}
async function deliveryRevokePromotion(req, res) {
const data = await proxyService.deliveryRevokePromotion({
promotionId: req.params.promotionId
});
return res.json({ success: true, data });
}
async function deliveryGetPromotion(req, res) {
const data = await proxyService.deliveryGetPromotion({
promotionId: req.params.promotionId
});
return res.json({ success: true, data });
}
async function deliveryListPromotions(req, res) {
const schema = z.object({
state: z.enum(["active", "pending", "completed", "revoked", "expired", "deleted"]).optional(),
start_time: z.string().optional(),
end_time: z.string().optional()
});
const query = schema.parse(req.query || {});
const data = await proxyService.deliveryListPromotions({
storeId: req.params.storeId,
query
});
return res.json({ success: true, data });
}
module.exports = {
genericProxy,
upsertMenu,
replaceMenu,
updateMenuItems,
getMenu,
listOrders,
getOrderById,
orderAction,
resolveFulfillmentIssues,
markOrderReady,
updateHours,
listProvisionableStores,
listStores,
getStoreById,
getStoreStatus,
setStoreStatus,
getHolidayHours,
setHolidayHours,
createPosData,
getPosData,
patchPosData,
deletePosData,
deliveryListStores,
deliveryGetStoreDetails,
deliveryUpdateStore,
deliveryGetStoreStatus,
deliverySetStoreStatus,
deliveryUpdatePrepTime,
deliveryUpdateFulfillmentConfig,
deliveryGetOrderDetails,
deliveryListOrders,
deliveryAcceptOrder,
deliveryDenyOrder,
deliveryCancelOrder,
deliveryMarkOrderReady,
deliveryAdjustOrderPrice,
deliveryUpdateReadyTime,
deliveryResolveFulfillmentIssues,
deliveryGetReplacementRecommendations,
deliveryUpdatePartnerCount,
deliveryByocIngestCourierLocation,
deliveryCreatePromotion,
deliveryRevokePromotion,
deliveryGetPromotion,
deliveryListPromotions
};