Funkcje XMLtoGraphQL

This commit is contained in:
2025-12-04 14:58:52 +01:00
parent c178a0e695
commit 69452672d4
4 changed files with 471 additions and 0 deletions

16
XMLtoGraphQL/readme.md Normal file
View File

@@ -0,0 +1,16 @@
# XMLtoGraphQL #
Function and xslt skeleton to turn XML products data to same as you would get from graphQL api
## Warning ##
Not every single piece of data is being handled by that function.
## Usage ##
With JS code you only need to put functions in script.js inside your code and then use idmGetGraphQLData to get graphQL'like data
```
const data = await idmGetGraphQLData("/pl/menu/perfumy/perfumy-meskie-944.html");
console.log(data);
```
With XSLT you will get script tag with JSON data inside. You just need to read that in your function
Created by • **[IdoMods](https://idomods.pl/)** • 2025

328
XMLtoGraphQL/script.js Normal file
View File

@@ -0,0 +1,328 @@
function idmGetGrossNetPrices({priceNode, name}){
return {
gross: {
value: priceNode?.getAttribute(`${name}`) !== null ? +priceNode.getAttribute(`${name}`) : undefined,
formatted: priceNode?.getAttribute(`${name}_formatted`) ?? undefined,
},
net: {
value: priceNode?.getAttribute(`${name}_net`) !== null ? +priceNode.getAttribute(`${name}_net`) : undefined,
formatted: priceNode?.getAttribute(`${name}_net_formatted`) ?? undefined,
}
}
}
function idmGetPriceFromNode(priceNode){
const priceObj = {
price: {
gross: {
value: priceNode?.getAttribute("value") !== null ? +priceNode.getAttribute("value") : undefined,
formatted: priceNode?.getAttribute("price_formatted") ?? undefined,
},
net: {
value: priceNode?.getAttribute("price_net") !== null ? +priceNode.getAttribute("price_net") : undefined,
formatted: priceNode?.getAttribute("price_net_formatted") ?? undefined,
}
},
rebateCodeActive: priceNode?.getAttribute("rebate_code_active") === "y" ? true : false,
omnibusPrice: {
...idmGetGrossNetPrices({priceNode, name: "omnibus_price"})
},
// depositPrice: {},
// depositPriceUnit: {},
// totalDepositPrice: {},
// totalDepositPriceUnit: {},
omnibusPriceDetails: {
// unit: {},
youSave: {
...idmGetGrossNetPrices({priceNode, name: "omnibus_yousave"})
},
youSavePercent: priceNode?.getAttribute("price_net") !== null ? +priceNode.getAttribute("omnibus_yousave_percent") : undefined,
omnibusPriceIsHigherThanSellingPrice: priceNode?.getAttribute("omnibus_price_is_higher_than_selling_price") === "true" ? true : false,
// newPriceEffectiveUntil: {},
},
tax: {
// worth: {},
vatPercent: priceNode?.getAttribute("tax") !== null ? +priceNode.getAttribute("tax") : undefined,
// vatString: ""
},
beforeRebate: {
...idmGetGrossNetPrices({priceNode, name: "beforerebate"})
},
// beforeRebateDetails: {
// youSave: {
// },
// youSavePercent: "",
// unit: {}
// },
// crossedPrice: {},
youSave: {
...idmGetGrossNetPrices({priceNode, name: "yousave"})
},
youSavePercent: priceNode?.getAttribute("yousave_percent") !== null ? +priceNode.getAttribute("yousave_percent") : undefined,
// unit: {},
max: {
...idmGetGrossNetPrices({priceNode, name: "maxprice"})
},
// maxPriceUnit: {},
// suggested: {},
unitConvertedPrice: {
...idmGetGrossNetPrices({priceNode, name: "unit_converted_price"})
},
// rebateNumber: {},
lastPriceChangeDate: priceNode?.getAttribute("last_price_change_date") ?? undefined,
// advancePrice: {},
promotionDuration: {
promotionTill: {
date: {
date: priceNode?.getAttribute("last_price_change_date")?.split("-")?.[2] ?? undefined,
month: priceNode?.getAttribute("last_price_change_date")?.split("-")?.[1] ?? undefined,
year: priceNode?.getAttribute("last_price_change_date")?.split("-")?.[0] ?? undefined,
// weekDay: "",
// formatted: ""
},
time: {
hour: priceNode?.getAttribute("promotiontillhour")?.split(":")?.[0] ?? undefined,
minutes: priceNode?.getAttribute("promotiontillhour")?.split(":")?.[1] ?? undefined,
seconds: priceNode?.getAttribute("promotiontillhour")?.split(":")?.[2] ?? undefined,
},
// timestamp: ""
},
// discountTill: {},
// distinguishedTill: {},
// specialTill: {},
},
// subscriptionPrice: {},
};
return priceObj;
}
function idmGetTraitsFromNode(node){
const awardedParameters = [];
node.querySelectorAll(":scope > trait").forEach(trait=>{
const currParameter = awardedParameters.find(param=>param.id === trait.getAttribute("groupid")) || {
id: trait.getAttribute("groupid") ?? undefined,
name: trait.getAttribute("groupdescription") ?? undefined,
// description: "",
values: [],
// contextValue: "",
// search: {
// icon: "",
// },
}
currParameter.values.push({
id: trait.getAttribute("traitid") ?? undefined,
name: trait.getAttribute("traitid") ?? undefined,
link: trait.getAttribute("link") ?? undefined,
// description: "",
// search: {
// icon: ""
// }
})
if(currParameter.values.length === 1) awardedParameters.push(currParameter);
});
return awardedParameters;
}
async function idmGetGraphQLData(link){
try{
const res = await fetch(`${link}${link.includes("?") ? "&getProductXML=t" : "?getProductXML=t"}`);
const str = await res.text();
const xml = new window.DOMParser().parseFromString(str, "text/xml");
const now = new Date();
const allProducts = xml.querySelectorAll("product");
if(allProducts.length === 0) throw new Error("Nie znaleziono produktów");
const data = [];
allProducts.forEach(prod=>{
// NODES
const priceNode = prod.querySelector(":scope >price");
const firmNode = prod.querySelector(":scope > firm");
const categoryNode = prod.querySelector(":scope > category");
const seriesNode = prod.querySelector(":scope > series");
const commentsNode = prod.querySelector(":scope > comments");
const sizesNode = prod.querySelector(":scope > sizes");
const versionsNode = prod.querySelector(":scope > versions");
// STREFY
const zones = [];
if(prod.getAttribute("promo") === "yes") zones.push("promotion");
if(prod.getAttribute("discount") === "yes") zones.push("discount");
if(prod.getAttribute("distinguished") === "yes") zones.push("distinguished");
if(prod.getAttribute("special") === "yes") zones.push("special");
if(prod.getAttribute("new") === "yes") zones.push("new");
if(prod.getAttribute("bestseller") === "yes") zones.push("bestseller");
if(prod.getAttribute("subscription") === "yes") zones.push("subscription");
// ZDJĘCIA
const enclosuresImages = [];
prod.querySelectorAll(":scope > enclosures > images > enclosure").forEach(img=>{
enclosuresImages.push({
position: img.getAttribute("position") ?? undefined,
type: img.getAttribute("type") ?? undefined,
typeSecond: img.getAttribute("type_second") ?? undefined,
url: img.getAttribute("url") ?? undefined,
urlSecond: img.getAttribute("url_second") ?? undefined,
width: img.getAttribute("width") ?? undefined,
height: img.getAttribute("height") ?? undefined,
iconUrl: img.getAttribute("icon") ?? undefined,
iconUrlSecond: img.getAttribute("icon_second") ?? undefined,
iconWidth: img.getAttribute("icon_width") ?? undefined,
iconHeight: img.getAttribute("icon_height") ?? undefined,
mediumUrl: img.getAttribute("medium") ?? undefined,
mediumUrlSecond: img.getAttribute("medium_second") ?? undefined,
mediumWidth: img.getAttribute("medium_width") ?? undefined,
mediumHeight: img.getAttribute("medium_height") ?? undefined,
})
})
// SIZES
const sizes = [];
sizesNode.querySelectorAll(":scope > size").forEach(size=>{
const availabilityNode = size.querySelector(":scope > availability");
const shippingTimeNode = availabilityNode.querySelector(":scope > shipping_time");
const sizePriceNode = availabilityNode.querySelector(":scope > price");
const weightNode = size.querySelector(":scope > weight");
sizes.push({
id: size?.getAttribute("type") ?? undefined,
name: size?.getAttribute("name") ?? undefined,
code: size?.getAttribute("code") ?? undefined,
codeProducer: size?.getAttribute("code_producer") ?? undefined,
codeExtern: size?.getAttribute("code_extern") ?? undefined,
amount: size?.getAttribute("amount") !== null ? +size.getAttribute("amount") : undefined,
amount_mo: size?.getAttribute("amount_mo") !== null ? +size.getAttribute("amount_mo") : undefined,
amount_mw: size?.getAttribute("amount_mw") !== null ? +size.getAttribute("amount_mw") : undefined,
amount_mp: size?.getAttribute("amount_mp") !== null ? +size.getAttribute("amount_mp") : undefined,
weight: weightNode?.getAttribute("g") !== null ? +weightNode.getAttribute("g") : undefined,
availability: {
visible: availabilityNode?.getAttribute("visible") === "y" ? true : false,
description: availabilityNode?.getAttribute("status_description") ?? undefined,
status: availabilityNode?.getAttribute("status") ?? undefined,
icon: availabilityNode?.getAttribute("status_gfx") ?? undefined,
deliveryDate: shippingTimeNode?.getAttribute("today") === "true" ? `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate().toString().padStart(2, 0)}` : availabilityNode?.getAttribute("delivery_date"),
minimumStockOfProduct: availabilityNode?.getAttribute("minimum_stock_of_product") ?? undefined,
// descriptionTel: "",
// iconTel: "",
},
shipping: {
today: shippingTimeNode?.getAttribute("today") === "true" ? true : false,
},
price: idmGetPriceFromNode(sizePriceNode),
// amountWholesale: "",
})
})
// VERSIONS
const versions = [];
versionsNode.querySelectorAll(":scope > version").forEach(ver=>{
versions.push({
id: ver?.getAttribute("id") !== null ? +ver?.getAttribute("id") : undefined,
name: ver?.getAttribute("name") ?? undefined,
icon: ver?.getAttribute("gfx") ?? undefined,
iconSecond: ver?.getAttribute("gfx_second") ?? undefined,
iconSmall: ver?.getAttribute("gfx_small") ?? undefined,
iconSmallSecond: ver?.getAttribute("gfx_small_second") ?? undefined,
productIcon: ver?.getAttribute("icon") ?? undefined,
productIconSecond: ver?.getAttribute("icon_second") ?? undefined,
productIconSmall: ver?.getAttribute("icon_small") ?? undefined,
productIconSmallSecond: ver?.getAttribute("icon_small_second") ?? undefined,
link: ver?.getAttribute("link") ?? undefined,
// parameterValues: [],
});
});
// DATA
const typeParts = prod.getAttribute("product_type");
data.push({
id: prod?.getAttribute("id") !== null ? +prod?.getAttribute("id") : undefined,
type: typeParts[1] === "item" ? typeParts[0] : typeParts[1],
code: prod?.getAttribute("code") ?? undefined,
name: prod.querySelector(":scope > name")?.textContent ?? undefined,
versionName: prod.querySelector(":scope > versions")?.getAttribute("name") ?? undefined,
description: prod.querySelector(":scope > description")?.textContent ?? undefined,
// longDescription: prod.querySelector("vlongdescription")?.textContent ?? undefined,
// longDescriptionSections: "",
link: prod?.getAttribute("link"),
zones,
icon: prod.querySelector(":scope > icon")?.textContent ?? undefined,
iconSecond: prod.querySelector(":scope > icon_second")?.textContent ?? undefined,
iconSmall: prod.querySelector(":scope > icon_small")?.textContent ?? undefined,
iconSmallSecond: prod.querySelector(":scope > icon_small_second")?.textContent ?? undefined,
price: idmGetPriceFromNode(priceNode),
unit: {
id: sizesNode?.getAttribute("unit_id") ?? undefined,
// name: "",
singular: sizesNode?.getAttribute("unit_single") !== null ? +sizesNode?.getAttribute("unit_single") : undefined,
plural: sizesNode?.getAttribute("unit_plural") ?? undefined,
fraction: sizesNode?.getAttribute("unit_fraction") ?? undefined,
sellBy: sizesNode?.getAttribute("unit_sellby") !== null ? +sizesNode?.getAttribute("unit_sellby") : undefined,
precision: sizesNode?.getAttribute("unit_precision") !== null ? +sizesNode?.getAttribute("unit_precision") : undefined,
unitConvertedFormat: sizesNode?.getAttribute("unit_converted_format") ?? undefined,
},
producer: {
id: firmNode?.getAttribute("id") !== null ? +firmNode?.getAttribute("id") : undefined,
name: firmNode?.getAttribute("name") ?? undefined,
link: firmNode?.getAttribute("productslink") ?? undefined,
searchIcons: {
icon: firmNode?.getAttribute("icon") ?? undefined,
},
// projectorIcons: {},
},
category: {
id: categoryNode?.getAttribute("id") !== null ? +categoryNode?.getAttribute("id") : undefined,
name: categoryNode?.getAttribute("name") ?? undefined,
link: categoryNode?.getAttribute("productslink") ?? undefined
},
group: {
id: versionsNode?.getAttribute("id") !== null ? +versionsNode?.getAttribute("id") : undefined,
name: versionsNode?.getAttribute("name") ?? undefined,
displayAll: versionsNode?.getAttribute("display_all") === "true" ? true : false,
link: versionsNode?.getAttribute("link") ?? undefined,
versions,
groupParameters: idmGetTraitsFromNode(versionsNode.querySelector(":scope > groupParameters"))
},
opinion: {
rating: commentsNode?.getAttribute("avg") !== null ? +commentsNode?.getAttribute("avg") : undefined,
count: commentsNode?.getAttribute("count") !== null ? +commentsNode?.getAttribute("count") : undefined,
link: commentsNode?.getAttribute("link") ?? undefined
},
enclosuresImages,
// enclosuresAttachments: [],
series: {
id: seriesNode?.getAttribute("id") !== null ? +seriesNode?.getAttribute("id") : undefined,
name: seriesNode?.getAttribute("name") ?? undefined,
link: seriesNode?.getAttribute("link") ?? undefined
},
awardedParameters: idmGetTraitsFromNode(prod.querySelector(":scope > traits")),
// parameteresWithContext: [],
sizes,
points: priceNode?.getAttribute("points") !== null ? +priceNode?.getAttribute("points") : undefined,
pointsReceive: priceNode?.getAttribute("points_recive") !== null ? +priceNode?.getAttribute("points_recive") : undefined,
// subscription: {},
// bundled: [],
// responsibleEntity: {},
})
})
return data;
}catch(err){
console.error(err);
return null;
}
}
// const testData = await idmGetGraphQLData("/pl/menu/perfumy/perfumy-meskie-944.html");
// console.log(testData);

126
XMLtoGraphQL/stary.xslt Normal file
View File

@@ -0,0 +1,126 @@
<script type="application/json" id="blogRecommendedProducts">
[
<iaixsl:for-each select="page/cms_products/*">
{
"id": <iaixsl:value-of select="@id"/>,
"type": "<iaixsl:choose><iaixsl:when test="@product_type = 'product_item'">product</iaixsl:when><iaixsl:otherwise><iaixsl:value-of select="substring-after(@product_type, '_')"/>",</iaixsl:otherwise></iaixsl:choose>",
"code": "<iaixsl:value-of select="@code"/>",
"name": "<iaixsl:value-of select="name"/>",
"versionName": "<iaixsl:value-of select="@version_name"/>",
"description": "<iaixsl:value-of select="cleardescription"/>",
"link": "<iaixsl:value-of select="@link"/>",
"zones": [
<iaixsl:if test="@promo = 'true'">"promotion"<iaixsl:if test="@bestseller = 'true' or @new = 'true'">,</iaixsl:if></iaixsl:if>
<iaixsl:if test="@bestseller = 'true'">"bestseller"<iaixsl:if test="@new = 'true'">,</iaixsl:if></iaixsl:if>
<iaixsl:if test="@new = 'true'">"new"</iaixsl:if>
],
"icon": "<iaixsl:value-of select="icon"/>",
"iconSmall": "<iaixsl:value-of select="icon_small"/>",
"points": <iaixsl:choose><iaixsl:when test="price/@points &gt; 0"><iaixsl:value-of select="price/@points"/></iaixsl:when><iaixsl:otherwise>0</iaixsl:otherwise></iaixsl:choose>,
"price": {
<iaixsl:if test="/shop/rebatecode">"rebateCodeActive": true,</iaixsl:if>
"price": {
"gross": {
"value": <iaixsl:value-of select="price/@value"/>,
"formatted": "<iaixsl:value-of select="price/@price_formatted"/>"
}
},
"omnibusPrice": {
"gross": <iaixsl:choose><iaixsl:when test="price/@omnibus_price">{
"value": <iaixsl:value-of select="price/@omnibus_price"/>,
"formatted": "<iaixsl:value-of select="price/@omnibus_price_formatted"/>"
}</iaixsl:when><iaixsl:otherwise>null</iaixsl:otherwise></iaixsl:choose>
},
"omnibusPriceDetails": {
<iaixsl:if test="price/@omnibus_yousave_percent">
"youSavePercent": <iaixsl:value-of select="price/@omnibus_yousave_percent"/>,
"omnibusPriceIsHigherThanSellingPrice": <iaixsl:value-of select="price/@omnibus_price_is_higher_than_selling_price"/>
</iaixsl:if>
},
"max": {
"gross": <iaixsl:choose><iaixsl:when test="price/@maxprice">{
"value": <iaixsl:value-of select="price/@maxprice"/>,
"formatted": "<iaixsl:value-of select="price/@maxprice_formatted"/>"
}</iaixsl:when><iaixsl:otherwise>null</iaixsl:otherwise></iaixsl:choose>
},
"unit": {
"gross": <iaixsl:choose><iaixsl:when test="price/@price_unit">{
"value": <iaixsl:value-of select="price/@price_unit"/>,
"formatted": "<iaixsl:value-of select="price/@price_unit_formatted"/>"
}</iaixsl:when><iaixsl:otherwise>null</iaixsl:otherwise></iaixsl:choose>
},
"unitConvertedPrice": {
"gross": <iaixsl:choose><iaixsl:when test="price/@unit_converted_price">{
"value": <iaixsl:value-of select="price/@unit_converted_price"/>,
"formatted": "<iaixsl:value-of select="price/@unit_converted_price_formatted"/>"
}</iaixsl:when><iaixsl:otherwise>null</iaixsl:otherwise></iaixsl:choose>
},
<iaixsl:if test="price/@yousave_percent">"youSavePercent": <iaixsl:value-of select="price/@yousave_percent"/>,</iaixsl:if>
"beforeRebate": {
"gross": <iaixsl:choose><iaixsl:when test="price/@beforerebate">{
"value": <iaixsl:value-of select="price/@beforerebate"/>,
"formatted": "<iaixsl:value-of select="price/@beforerebate_formatted"/>"
}</iaixsl:when><iaixsl:otherwise>null</iaixsl:otherwise></iaixsl:choose>
}
},
"opinion": {
"rating": 0,
"count": 0
},
"category": {
"name": "<iaixsl:value-of select="category/@name"/>"
},
"awardedParameters": [
<iaixsl:for-each select="traits/trait[not(@groupid = preceding-sibling::trait/@groupid)]">
{
"id": "<iaixsl:value-of select="@groupid"/>",
"name": "<iaixsl:value-of select="@groupdescription"/>",
"description": "<iaixsl:value-of select="description"/>",
"values": [
<iaixsl:for-each select="../trait[@groupid = current()/@groupid]">
{
"id": "<iaixsl:value-of select="@traitid"/>",
"name": "<iaixsl:value-of select="@traitdescription"/>"
}<iaixsl:if test="last() != position()">,</iaixsl:if>
</iaixsl:for-each>
]
}<iaixsl:if test="last() != position()">,</iaixsl:if>
</iaixsl:for-each>
],
"sizes": [
<iaixsl:for-each select="sizes/size">
{
"id": "<iaixsl:value-of select="@type"/>",
"amount": <iaixsl:value-of select="@amount"/>
}<iaixsl:if test="last() != position()">,</iaixsl:if>
</iaixsl:for-each>
],
"enclosuresImages": [
<iaixsl:for-each select="enclosures/images/enclosure">
{
"position": <iaixsl:value-of select="@position"/>,
"type": "<iaixsl:value-of select="@type"/>",
"url": "<iaixsl:value-of select="@url"/>",
"width": <iaixsl:value-of select="@width"/>,
"height": <iaixsl:value-of select="@height"/>,
"iconUrl": "<iaixsl:value-of select="@icon"/>",
"iconWidth": <iaixsl:value-of select="@icon_width"/>,
"iconHeight": <iaixsl:value-of select="@icon_height"/>,
"mediumUrl": "<iaixsl:value-of select="@medium"/>",
"mediumWidth": <iaixsl:value-of select="@medium_width"/>,
"mediumHeight": <iaixsl:value-of select="@medium_height"/>
}<iaixsl:if test="last() != position()">,</iaixsl:if>
</iaixsl:for-each>
],
"producer": {
"id": "<iaixsl:value-of select="substring-before(substring-after(firm/@icon, '/gfx/projector/'), '_1.jpg')"/>",
"name": "<iaixsl:value-of select="firm/@name"/>",
"link": "<iaixsl:value-of select="firm/@productslink"/>",
"searchIcons": {
"icon": "<iaixsl:value-of select="firm/@icon"/>"
}
}
}<iaixsl:if test="last() != position()">,</iaixsl:if>
</iaixsl:for-each>
]
</script>

View File

@@ -6,6 +6,7 @@
position: relative;
&__info_icon{
color: var(--tooltip-border);
display: inline-block;
&:before{
content: "";
display: block;