delete/create api

This commit is contained in:
2024-03-25 17:00:56 -05:00
parent 5b75f8bb9a
commit 7646ee0fc8
3 changed files with 255 additions and 78 deletions

View File

@@ -1,65 +1,205 @@
import fetch from "node-fetch"; import fetch from "node-fetch";
import fetchEbayUserToken from "../utils/fetchEbayUserToken.js"; import fetchEbayUserToken from "../utils/fetchEbayUserToken.js";
export const addItem = async (req, res) => { export const createAndListItem = async (req, res) => {
const itemDetails = req.body; const {
// must be called with req/res due to cookie access - cookie flow is inside fetchEbayUserToken() sku,
const token = await fetchEbayUserToken(req, res) marketplaceId,
format,
availableQuantity,
categoryId,
condition,
listingDescription,
pricingSummary,
merchantLocationKey,
quantityLimitPerBuyer,
product, // Extract the entire product object
packageWeightAndSize, // Adjusted to packageWeightAndSize from the refined payload
imageUrls,
} = req.body;
// Constructing the payload for the Inventory API const token = await fetchEbayUserToken(req, res); // Get the eBay user token
const offerPayload = {
sku: itemDetails.sku,
marketplaceId: itemDetails.marketplaceId,
format: itemDetails.format,
listingDescription: itemDetails.listingDescription,
availableQuantity: itemDetails.availableQuantity,
categoryId: itemDetails.categoryId,
listingPolicies: {
paymentPolicyId: itemDetails.listingPolicies.paymentPolicyId,
fulfillmentPolicyId: itemDetails.listingPolicies.fulfillmentPolicyId,
returnPolicyId: itemDetails.listingPolicies.returnPolicyId,
},
pricingSummary: {
price: {
currency: itemDetails.pricingSummary.price.currency,
value: itemDetails.pricingSummary.price.value,
},
},
merchantLocationKey: itemDetails.merchantLocationKey,
quantityLimitPerBuyer: itemDetails.quantityLimitPerBuyer,
};
let dynamicAspects = {};
for (const [key, value] of Object.entries(product.aspects)) {
dynamicAspects[key] = Array.isArray(value) ? value : [value];
}
try { try {
const response = await fetch( // Create the inventory item
"https://api.ebay.com/sell/inventory/v1/offer", const inventoryItemPayload = {
sku,
condition,
product: {
title: product.title.substring(0, 80), // Utilizing product title
brand: product.brand, // Using brand from the product object
description: product.description, // Using product description
imageUrls: product.imageUrls, // Using all imageUrls from product
aspects: dynamicAspects
},
availability: {
shipToLocationAvailability: {
quantity: availableQuantity,
},
},
// Package weight and dimensions, adjusted to the new structure
packageWeightAndSize: {
weight: {
value: parseFloat(packageWeightAndSize.weight.value),
unit: packageWeightAndSize.weight.unit,
},
dimensions: {
length: parseFloat(packageWeightAndSize.dimensions.length),
width: parseFloat(packageWeightAndSize.dimensions.width),
height: parseFloat(packageWeightAndSize.dimensions.height),
unit: packageWeightAndSize.dimensions.unit,
},
},
};
const createItemResponse = await fetch(
`https://api.ebay.com/sell/inventory/v1/inventory_item/${sku}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
"Content-Language": "en-US",
"Authorization": `Bearer ${token}`,
"Accept": "application/json",
},
body: JSON.stringify(inventoryItemPayload),
}
);
// Optionally, you can parse the response body if eBay's API returns useful information in it
const itemResponseData = await createItemResponse;
console.log(itemResponseData);
// 2. Create the offer for the inventory item
const offerPayload = {
sku,
marketplaceId,
format,
listing: {
categoryId,
listingDescription,
merchantLocationKey,
},
pricingSummary,
quantityLimitPerBuyer,
};
const offerResponse = await fetch(
`https://api.ebay.com/sell/inventory/v1/offer`,
{ {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
"Content-Language": "en-US", "Content-Language": "en-US",
"Authorization": `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
body: JSON.stringify(offerPayload), body: JSON.stringify(offerPayload),
} }
); );
console.log(response)
if (!response.ok) {
const errorBody = await response.text(); // Get the response body as text
console.error(`eBay API responded with status ${response.status}: ${errorBody}`);
throw new Error(`eBay API responded with status ${response.status}: ${errorBody}`);
}
if (!offerResponse.ok) {
// Handle error in creating the offer
const errorBody = await offerResponse.text();
throw new Error(
`Failed to create offer: ${offerResponse.status} - ${errorBody}`
);
}
const responseData = await response.json(); const offerData = await offerResponse.json();
res.json({ success: true, data: responseData }); const offerId = offerData.offerId;
// 3. Publish the offer to convert it into a live listing
await fetch(
`https://api.ebay.com/sell/inventory/v1/offer/${offerId}/publish/`,
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
}
);
res.json({ success: true, message: "Item listed successfully" });
} catch (error) { } catch (error) {
console.error("Error adding item to eBay via Inventory API:", error); console.error("Error in createAndListItem endpoint:", error);
console.log(error.message);
res.status(500).json({ res.status(500).json({
success: false, success: false,
message: "Failed to add item to eBay via Inventory API", message: "Failed to create and list item on eBay",
error: error.message, error: error.message,
}); });
} }
}; };
export const deleteItemBySku = async (req, res) => {
const { sku } = req.params; // Assuming SKU is provided as a URL parameter
try {
const token = await fetchEbayUserToken(req, res); // Get the eBay user token
// Perform the deletion of the inventory item
const deleteResponse = await fetch(`https://api.ebay.com/sell/inventory/v1/inventory_item/${sku}`, {
method: "DELETE",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
"Accept": "application/json",
},
});
if (!deleteResponse.ok) {
// Handle unsuccessful deletion
const errorBody = await deleteResponse.text();
throw new Error(`Failed to delete inventory item: ${deleteResponse.status} - ${errorBody}`);
}
// Successfully deleted the item
res.json({ success: true, message: `Item with SKU: ${sku} deleted successfully.` });
} catch (error) {
console.error("Error in deleteItemBySku endpoint:", error);
res.status(500).json({
success: false,
message: `Failed to delete item with SKU: ${sku} from eBay`,
error: error.message,
});
}
};
export const getAllInventory = async (req, res) => {
const limit = req.query.limit || '100'; // Set default limit
const offset = req.query.offset || '0'; // Set default offset
try {
const token = await fetchEbayUserToken(req, res); // Assuming fetchEbayUserToken doesn't need req, res passed and handles token internally
const response = await fetch(`https://api.ebay.com/sell/inventory/v1/inventory_item?limit=${limit}&offset=${offset}`, {
method: 'GET',
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`,
"Accept-Language": "en-US",
},
});
if (!response.ok) {
// If the response is not okay, throw an error with status and statusText
throw new Error(`Failed to retrieve inventory items: ${response.status} ${response.statusText}`);
}
const inventoryItems = await response.json(); // Parsing the JSON body of the response
res.json(inventoryItems); // Sending the inventory items back to the client
} catch (error) {
console.error('Error fetching inventory:', error);
res.status(500).json({
error: 'Internal Server Error',
message: error.message,
});
}
};

View File

@@ -1,11 +1,12 @@
// routes/dataRoutes.js // routes/dataRoutes.js
import express from "express"; import express from "express";
import { addItem } from "../controllers/inventoryController.js"; import { createAndListItem, deleteItemBySku, getAllInventory } from "../controllers/inventoryController.js";
const router = express.Router(); const router = express.Router();
router.post("/add-item", addItem); router.post("/create-list-item", createAndListItem);
router.get("/delete-item-by-sku", deleteItemBySku);
router.get("/get-all-inventory", getAllInventory)
// You can add more data-related routes here in the future
export default router; export default router;

View File

@@ -4,7 +4,8 @@ import cors from "cors";
import dotenv from "dotenv"; import dotenv from "dotenv";
import dataRoutes from "./routes/dataRoutes.js"; import dataRoutes from "./routes/dataRoutes.js";
import inventoryRoutes from "./routes/inventoryRoutes.js"; import inventoryRoutes from "./routes/inventoryRoutes.js";
import cookieParser from 'cookie-parser'; import cookieParser from "cookie-parser";
import fetchEbayUserToken from "./utils/fetchEbayUserToken.js";
dotenv.config(); dotenv.config();
@@ -17,6 +18,35 @@ app.use(cookieParser());
app.use("/api/data", dataRoutes); app.use("/api/data", dataRoutes);
app.use("/api/inventory", inventoryRoutes); app.use("/api/inventory", inventoryRoutes);
app.get("/check-inventory", async (req, res) => {
try {
const token = "v^1.1#i^1#r^0#p^3#f^0#I^3#t^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"
const limit = req.query.limit || '100'; // Default limit to 10 if not specified
const offset = req.query.offset || '0'; // Default offset to 0 if not specified
const response = await fetch(`https://api.ebay.com/sell/inventory/v1/inventory_item?limit=${limit}&offset=${offset}`, {
method: 'GET',
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`,
"Accept-Language": "en-US", // Explicitly setting the Accept-Language header
},
});
console.log(response)
const inventoryItems = await response.json();
console.log(inventoryItems);
res.json(inventoryItems);
} catch (error) {
console.error('Error checking inventory:', error);
if (!res.headersSent) {
res.status(500).json({ error: 'Internal Server Error' });
}
}
});
/* /*
the below code needs to be encapsulated somehow. It requires a manual flow, including QUICKLY copy/pasting the code query string from the result of localhost:300/auth/ebay (below) the below code needs to be encapsulated somehow. It requires a manual flow, including QUICKLY copy/pasting the code query string from the result of localhost:300/auth/ebay (below)
@@ -32,42 +62,48 @@ app.use("/api/inventory", inventoryRoutes);
2. Add a fetchEbayUserToken endpoint and figure how to cycle user tokens every 2 hours with refresh token 2. Add a fetchEbayUserToken endpoint and figure how to cycle user tokens every 2 hours with refresh token
3. Add refresh token to .env and figure out how to safely store user tokens serverside (cookie? knowledge gap here) 3. Add refresh token to .env and figure out how to safely store user tokens serverside (cookie? knowledge gap here)
*/ */
app.get('/auth/ebay', async (req, res) => { app.get("/auth/ebay", async (req, res) => {
// the below URL is hardcoded because it's static in the eBay dev dashboard // the below URL is hardcoded because it's static in the eBay dev dashboard
const authUrl = `https://auth.ebay.com/oauth2/authorize?client_id=TylerPul-ebayimpo-PRD-a983027cf-9b6b8bba&response_type=code&redirect_uri=Tyler_Pulse-TylerPul-ebayim-ledkmyo&scope=https://api.ebay.com/oauth/api_scope https://api.ebay.com/oauth/api_scope/sell.marketing.readonly https://api.ebay.com/oauth/api_scope/sell.marketing https://api.ebay.com/oauth/api_scope/sell.inventory.readonly https://api.ebay.com/oauth/api_scope/sell.inventory https://api.ebay.com/oauth/api_scope/sell.account.readonly https://api.ebay.com/oauth/api_scope/sell.account https://api.ebay.com/oauth/api_scope/sell.fulfillment.readonly https://api.ebay.com/oauth/api_scope/sell.fulfillment https://api.ebay.com/oauth/api_scope/sell.analytics.readonly https://api.ebay.com/oauth/api_scope/sell.finances https://api.ebay.com/oauth/api_scope/sell.payment.dispute https://api.ebay.com/oauth/api_scope/commerce.identity.readonly https://api.ebay.com/oauth/api_scope/sell.reputation https://api.ebay.com/oauth/api_scope/sell.reputation.readonly https://api.ebay.com/oauth/api_scope/commerce.notification.subscription https://api.ebay.com/oauth/api_scope/commerce.notification.subscription.readonly https://api.ebay.com/oauth/api_scope/sell.stores https://api.ebay.com/oauth/api_scope/sell.stores.readonly`; const authUrl = `https://auth.ebay.com/oauth2/authorize?client_id=TylerPul-ebayimpo-PRD-a983027cf-9b6b8bba&response_type=code&redirect_uri=Tyler_Pulse-TylerPul-ebayim-ledkmyo&scope=https://api.ebay.com/oauth/api_scope https://api.ebay.com/oauth/api_scope/sell.marketing.readonly https://api.ebay.com/oauth/api_scope/sell.marketing https://api.ebay.com/oauth/api_scope/sell.inventory.readonly https://api.ebay.com/oauth/api_scope/sell.inventory https://api.ebay.com/oauth/api_scope/sell.account.readonly https://api.ebay.com/oauth/api_scope/sell.account https://api.ebay.com/oauth/api_scope/sell.fulfillment.readonly https://api.ebay.com/oauth/api_scope/sell.fulfillment https://api.ebay.com/oauth/api_scope/sell.analytics.readonly https://api.ebay.com/oauth/api_scope/sell.finances https://api.ebay.com/oauth/api_scope/sell.payment.dispute https://api.ebay.com/oauth/api_scope/commerce.identity.readonly https://api.ebay.com/oauth/api_scope/sell.reputation https://api.ebay.com/oauth/api_scope/sell.reputation.readonly https://api.ebay.com/oauth/api_scope/commerce.notification.subscription https://api.ebay.com/oauth/api_scope/commerce.notification.subscription.readonly https://api.ebay.com/oauth/api_scope/sell.stores https://api.ebay.com/oauth/api_scope/sell.stores.readonly`;
res.redirect(authUrl); // Redirect the user to eBay's sign-in page res.redirect(authUrl); // Redirect the user to eBay's sign-in page
}); });
// Step 2: Handle the redirect from eBay // Step 2: Handle the redirect from eBay
app.get('/auth/ebay/callback', async (req, res) => { app.get("/auth/ebay/callback", async (req, res) => {
// this code comes from a query string at localhost:3000/auth/ebay when we redirect // this code comes from a query string at localhost:3000/auth/ebay when we redirect
const code = 'v%5E1.1%23i%5E1%23p%5E3%23f%5E0%23r%5E1%23I%5E3%23t%5EUl41Xzc6NUE2ODc0NkU5Q0Q4N0QxQjhENTVCNzAxQTAwMEM2MzlfMF8xI0VeMjYw' const code =
try { "v%5E1.1%23i%5E1%23p%5E3%23f%5E0%23r%5E1%23I%5E3%23t%5EUl41Xzc6NUE2ODc0NkU5Q0Q4N0QxQjhENTVCNzAxQTAwMEM2MzlfMF8xI0VeMjYw";
// Step 3: Exchange the authorization code for access and refresh tokens try {
const tokenResponse = await fetch('https://api.ebay.com/identity/v1/oauth2/token', { // Step 3: Exchange the authorization code for access and refresh tokens
method: 'POST', const tokenResponse = await fetch(
"https://api.ebay.com/identity/v1/oauth2/token",
{
method: "POST",
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded', "Content-Type": "application/x-www-form-urlencoded",
'Authorization': `Basic ${Buffer.from(`${process.env.EBAY_CLIENT_ID}:${process.env.EBAY_CLIENT_SECRET}`).toString('base64')}` Authorization: `Basic ${Buffer.from(
`${process.env.EBAY_CLIENT_ID}:${process.env.EBAY_CLIENT_SECRET}`
).toString("base64")}`,
}, },
// we use Tyler_Pulse-TylerPul-ebayim-ledkmyo and not the initial redirect URL // we use Tyler_Pulse-TylerPul-ebayim-ledkmyo and not the initial redirect URL
body: `grant_type=authorization_code&code=${code}&redirect_uri=Tyler_Pulse-TylerPul-ebayim-ledkmyo` body: `grant_type=authorization_code&code=${code}&redirect_uri=Tyler_Pulse-TylerPul-ebayim-ledkmyo`,
});
if (!tokenResponse.ok) {
throw new Error('Failed to exchange authorization code for tokens');
} }
);
const tokenData = await tokenResponse.json();
console.log('Access Token:', tokenData.access_token); if (!tokenResponse.ok) {
console.log('Refresh Token:', tokenData.refresh_token); throw new Error("Failed to exchange authorization code for tokens");
res.send('Authentication successful! Tokens acquired.'); // For demonstration purposes; you might want to redirect the user or show a different message
} catch (error) {
console.error('Error during token exchange:', error);
res.status(500).send('Internal Server Error');
} }
});
const tokenData = await tokenResponse.json();
console.log("Access Token:", tokenData.access_token);
console.log("Refresh Token:", tokenData.refresh_token);
res.send("Authentication successful! Tokens acquired."); // For demonstration purposes; you might want to redirect the user or show a different message
} catch (error) {
console.error("Error during token exchange:", error);
res.status(500).send("Internal Server Error");
}
});
const PORT = process.env.PORT || 3000; const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`)); app.listen(PORT, () => console.log(`Server running on port ${PORT}`));