When the fulfillment service already exists the create mutation returns userErrors. Now queries existing fulfillmentServices to get the location ID instead of falling through with null. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
166 lines
4.6 KiB
JavaScript
166 lines
4.6 KiB
JavaScript
const axios = require("axios");
|
|
const { log } = require("./logger");
|
|
|
|
const getLocationQuery = `
|
|
query {
|
|
locations(first: 1, query: "name:'Shop location'") {
|
|
nodes {
|
|
id
|
|
name
|
|
address {
|
|
address1
|
|
address2
|
|
city
|
|
province
|
|
provinceCode
|
|
country
|
|
countryCode
|
|
zip
|
|
phone
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
async function getStoreAddress(client) {
|
|
const response = await client.post("", { query: getLocationQuery });
|
|
const location = response.data?.data?.locations?.nodes?.[0];
|
|
return location?.address || null;
|
|
}
|
|
|
|
function createLocationMutation(address) {
|
|
return `
|
|
mutation {
|
|
locationAdd(input: {
|
|
name: "(App) Race Nation Distribution"
|
|
address: {
|
|
address1: "${address?.address1 || ""}"
|
|
address2: "${address?.address2 || ""}"
|
|
city: "${address?.city || ""}"
|
|
provinceCode: "${address?.provinceCode || "ON"}"
|
|
countryCode: ${JSON.stringify(address?.countryCode || "US")}
|
|
zip: "${address?.zip || ""}"
|
|
phone: "${address?.phone || ""}"
|
|
}
|
|
fulfillsOnlineOrders: true
|
|
}) {
|
|
location {
|
|
id
|
|
name
|
|
}
|
|
userErrors {
|
|
code
|
|
field
|
|
message
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
}
|
|
|
|
async function createCustomLocation(address, client) {
|
|
const response = await client.post("", { query: createLocationMutation(address) });
|
|
return response.data?.data?.locationAdd || { location: null, userErrors: [] };
|
|
}
|
|
|
|
async function createFulfillmentService(shop, accessToken) {
|
|
const client = axios.create({
|
|
baseURL: `https://${shop}/admin/api/2025-10/graphql.json`,
|
|
headers: {
|
|
"X-Shopify-Access-Token": accessToken,
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
|
|
const mutation = `
|
|
mutation {
|
|
fulfillmentServiceCreate(
|
|
name: "Race Nation Distribution"
|
|
callbackUrl: "https://backend.race-nation.com/fulfillment"
|
|
) {
|
|
fulfillmentService {
|
|
id
|
|
serviceName
|
|
callbackUrl
|
|
handle
|
|
location {
|
|
id
|
|
}
|
|
}
|
|
userErrors {
|
|
field
|
|
message
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
try {
|
|
log(shop, "Creating fulfillment service...");
|
|
const response = await client.post("", { query: mutation });
|
|
const data = response.data?.data?.fulfillmentServiceCreate;
|
|
|
|
let fulfillmentService = data?.fulfillmentService || null;
|
|
let locationId = fulfillmentService?.location?.id || null;
|
|
|
|
if (data?.userErrors?.length) {
|
|
// Service already exists — query it to get its locationId
|
|
log(shop, `Fulfillment service already exists, querying existing...`);
|
|
try {
|
|
const existingResp = await client.post("", {
|
|
query: `{ fulfillmentServices { id serviceName location { id name } } }`,
|
|
});
|
|
const existing = existingResp.data?.data?.fulfillmentServices || [];
|
|
const ours = existing.find((s) => s.serviceName === "Race Nation Distribution") || existing[0];
|
|
if (ours?.location?.id) {
|
|
locationId = ours.location.id;
|
|
fulfillmentService = ours;
|
|
log(shop, `Found existing fulfillment service location: ${ours.location.name} (${locationId})`);
|
|
}
|
|
} catch (queryErr) {
|
|
log(shop, `Could not query existing fulfillment services: ${queryErr.message}`);
|
|
}
|
|
}
|
|
|
|
if (!locationId) {
|
|
// Last resort: try to get shop's primary location
|
|
try {
|
|
const locResp = await client.post("", {
|
|
query: `{ locations(first: 1) { nodes { id name } } }`,
|
|
});
|
|
locationId = locResp.data?.data?.locations?.nodes?.[0]?.id || null;
|
|
if (locationId) log(shop, `Using primary store location: ${locationId}`);
|
|
} catch {}
|
|
}
|
|
|
|
if (!locationId && data?.userErrors?.length) {
|
|
return { success: false, errors: data.userErrors };
|
|
}
|
|
|
|
const address = await getStoreAddress(client).catch(() => null);
|
|
if (address && !locationId) {
|
|
const customLocation = await createCustomLocation(address, client);
|
|
if (!customLocation?.userErrors?.length && customLocation?.location?.id) {
|
|
locationId = customLocation.location.id;
|
|
}
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
fulfillmentService,
|
|
locationId,
|
|
};
|
|
} catch (error) {
|
|
log(shop, `Fulfillment service request failed: ${error.response ? JSON.stringify(error.response.data) : error.message}`);
|
|
return {
|
|
success: false,
|
|
error: error.response ? error.response.data : error.message,
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
createFulfillmentService,
|
|
};
|