From 99a1308398aacd4c3cedec3665619acbe709e9b9 Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Mon, 9 Oct 2023 21:52:36 -0300 Subject: [PATCH 01/19] feat: improved validation to support array values --- server/controllers/settings.js | 23 +++--- server/services/pattern.js | 141 ++++++++++++++++++++++----------- server/utils/index.js | 27 ++++--- 3 files changed, 124 insertions(+), 67 deletions(-) diff --git a/server/controllers/settings.js b/server/controllers/settings.js index f713447..b394510 100644 --- a/server/controllers/settings.js +++ b/server/controllers/settings.js @@ -1,6 +1,6 @@ -'use strict'; +"use strict"; -const { getService } = require('../utils'); +const { getService } = require("../utils"); /** * Sitemap.js controller @@ -10,26 +10,29 @@ const { getService } = require('../utils'); module.exports = { getSettings: async (ctx) => { - const config = await getService('settings').getConfig(); + const config = await getService("settings").getConfig(); ctx.send(config); }, updateSettings: async (ctx) => { - const config = await getService('settings').getConfig(); - const newContentTypes = Object.keys(ctx.request.body.contentTypes).filter((x) => !Object.keys(config.contentTypes).includes(x)); + const config = await getService("settings").getConfig(); + const newContentTypes = Object.keys(ctx.request.body.contentTypes).filter( + (x) => !Object.keys(config.contentTypes).includes(x) + ); + console.log("[updateSettings] newContentTypes", newContentTypes); await strapi .store({ - environment: '', - type: 'plugin', - name: 'sitemap', + environment: "", + type: "plugin", + name: "sitemap", }) - .set({ key: 'settings', value: ctx.request.body }); + .set({ key: "settings", value: ctx.request.body }); // Load lifecycle methods for auto generation of sitemap. await newContentTypes.map(async (contentType) => { - await getService('lifecycle').loadLifecycleMethod(contentType); + await getService("lifecycle").loadLifecycleMethod(contentType); }); ctx.send({ ok: true }); diff --git a/server/services/pattern.js b/server/services/pattern.js index bf658b0..712b972 100644 --- a/server/services/pattern.js +++ b/server/services/pattern.js @@ -1,6 +1,6 @@ -'use strict'; +"use strict"; -const { logMessage } = require('../utils'); +const { logMessage } = require("../utils"); /** * Pattern service. @@ -16,25 +16,29 @@ const { logMessage } = require('../utils'); */ const getAllowedFields = (contentType, allowedFields = []) => { const fields = []; - const fieldTypes = allowedFields.length > 0 ? allowedFields : strapi.config.get('plugin.sitemap.allowedFields'); + const fieldTypes = + allowedFields.length > 0 + ? allowedFields + : strapi.config.get("plugin.sitemap.allowedFields"); fieldTypes.map((fieldType) => { Object.entries(contentType.attributes).map(([fieldName, field]) => { - if ((field.type === fieldType || fieldName === fieldType) && field.type !== 'relation') { + // console.warn("[getAllowedFields] fieldName", fieldName, "field", field); + if ( + (field.type === fieldType || fieldName === fieldType) && + field.type !== "relation" + ) { fields.push(fieldName); } else if ( - field.type === 'relation' - && field.target - && field.relation.endsWith('ToOne') // TODO: implement `ToMany` relations (#78). - && fieldName !== 'localizations' - && fieldName !== 'createdBy' - && fieldName !== 'updatedBy' + field.type === "relation" && + field.target && + field.relation.endsWith("ToOne") && // TODO: implement `ToMany` relations (#78). + fieldName !== "localizations" && + fieldName !== "createdBy" && + fieldName !== "updatedBy" ) { const relation = strapi.contentTypes[field.target]; - if ( - fieldTypes.includes('id') - && !fields.includes(`${fieldName}.id`) - ) { + if (fieldTypes.includes("id") && !fields.includes(`${fieldName}.id`)) { fields.push(`${fieldName}.id`); } @@ -44,16 +48,33 @@ const getAllowedFields = (contentType, allowedFields = []) => { } }); } else if ( - field.type === 'component' - && field.component - && field.repeatable !== true // TODO: implement repeatable components (#78). + field.type === "relation" && + field.target && + field.mappedBy && + field.relation.endsWith("ToMany") && + fieldName !== "localizations" && + fieldName !== "createdBy" && + fieldName !== "updatedBy" + ) { + const relation = strapi.contentTypes[field.target]; + + // if (fieldTypes.includes("id") && !fields.includes(`${fieldName}.id`)) { + // fields.push(`${fieldName}[0].id`); + // } + + Object.entries(relation.attributes).map(([subFieldName, subField]) => { + if (subField.type === fieldType || subFieldName === fieldType) { + fields.push(`${fieldName}[0].${subFieldName}`); + } + }); + } else if ( + field.type === "component" && + field.component && + field.repeatable !== true // TODO: implement repeatable components (#78). ) { const relation = strapi.components[field.component]; - if ( - fieldTypes.includes('id') - && !fields.includes(`${fieldName}.id`) - ) { + if (fieldTypes.includes("id") && !fields.includes(`${fieldName}.id`)) { fields.push(`${fieldName}.id`); } @@ -67,14 +88,13 @@ const getAllowedFields = (contentType, allowedFields = []) => { }); // Add id field manually because it is not on the attributes object of a content type. - if (fieldTypes.includes('id')) { - fields.push('id'); + if (fieldTypes.includes("id")) { + fields.push("id"); } return fields; }; - /** * Get all fields from a pattern. * @@ -85,15 +105,31 @@ const getAllowedFields = (contentType, allowedFields = []) => { * @returns {array} The fields. */ const getFieldsFromPattern = (pattern, topLevel = false, relation = null) => { - let fields = pattern.match(/[[\w\d.]+]/g); // Get all substrings between [] as array. + console.log( + "[getFieldsFromPattern] pattern", + pattern, + "topLevel", + topLevel, + "relation", + relation + ); + + let fields = pattern.match(/(?<=\/)(\[.*?\])(?=\/|$)/g); // Get all substrings between [] as array. // PR - Now it works with [value[0].field] + + console.log("[getFieldsFromPattern] fields 1", fields); + // eslint-disable-next-line prefer-regex-literals - fields = fields.map((field) => RegExp(/(?<=\[)(.*?)(?=\])/).exec(field)[0]); // Strip [] from string. + fields = fields.map((field) => field.replace(/^.|.$/g, "")); // Strip [] from string. // PR - Simplify regex + console.log("[getFieldsFromPattern] fields 2", fields); if (relation) { - fields = fields.filter((field) => field.startsWith(`${relation}.`)); - fields = fields.map((field) => field.split('.')[1]); + fields = fields.filter( + (field) => + field.startsWith(`${relation}.`) || field.startsWith(`${relation}[0].`) + ); + fields = fields.map((field) => field.split(".")[1]); } else if (topLevel) { - fields = fields.filter((field) => field.split('.').length === 1); + fields = fields.filter((field) => field.split(".").length === 1); } return fields; @@ -108,8 +144,8 @@ const getFieldsFromPattern = (pattern, topLevel = false, relation = null) => { */ const getRelationsFromPattern = (pattern) => { let fields = getFieldsFromPattern(pattern); - fields = fields.filter((field) => field.split('.').length > 1); // Filter on fields containing a dot (.) - fields = fields.map((field) => field.split('.')[0]); // Extract the first part of the fields + fields = fields.filter((field) => field.split(".").length > 1); // Filter on fields containing a dot (.) + fields = fields.map((field) => field.split(".")[0]); // Extract the first part of the fields return fields; }; @@ -126,19 +162,28 @@ const resolvePattern = async (pattern, entity) => { const fields = getFieldsFromPattern(pattern); fields.map((field) => { - const relationalField = field.split('.').length > 1 ? field.split('.') : null; + const relationalField = + field.split(".").length > 1 ? field.split(".") : null; if (!relationalField) { - pattern = pattern.replace(`[${field}]`, entity[field] || ''); + pattern = pattern.replace(`[${field}]`, entity[field] || ""); } else if (Array.isArray(entity[relationalField[0]])) { - strapi.log.error(logMessage('Something went wrong whilst resolving the pattern.')); - } else if (typeof entity[relationalField[0]] === 'object') { - pattern = pattern.replace(`[${field}]`, entity[relationalField[0]] && entity[relationalField[0]][relationalField[1]] ? entity[relationalField[0]][relationalField[1]] : ''); + strapi.log.error( + logMessage("Something went wrong whilst resolving the pattern.") + ); + } else if (typeof entity[relationalField[0]] === "object") { + pattern = pattern.replace( + `[${field}]`, + entity[relationalField[0]] && + entity[relationalField[0]][relationalField[1]] + ? entity[relationalField[0]][relationalField[1]] + : "" + ); } }); - pattern = pattern.replace(/\/+/g, '/'); // Remove duplicate forward slashes. - pattern = pattern.startsWith('/') ? pattern : `/${pattern}`; // Make sure we only have on forward slash. + pattern = pattern.replace(/\/+/g, "/"); // Remove duplicate forward slashes. + pattern = pattern.startsWith("/") ? pattern : `/${pattern}`; // Make sure we only have on forward slash. return pattern; }; @@ -153,27 +198,33 @@ const resolvePattern = async (pattern, entity) => { * @returns {string} object.message Validation string. */ const validatePattern = async (pattern, allowedFieldNames) => { + console.log( + "[validatePattern] pattern", + pattern, + "allowedFieldNames", + allowedFieldNames + ); if (!pattern) { return { valid: false, - message: 'Pattern can not be empty', + message: "Pattern can not be empty", }; } - const preCharCount = pattern.split('[').length - 1; - const postCharount = pattern.split(']').length - 1; + const preCharCount = pattern.split("[").length - 1; + const postCharount = pattern.split("]").length - 1; if (preCharCount < 1 || postCharount < 1) { return { valid: false, - message: 'Pattern should contain at least one field', + message: "Pattern should contain at least one field", }; } if (preCharCount !== postCharount) { return { valid: false, - message: 'Fields in the pattern are not escaped correctly', + message: "Fields in the pattern are not escaped correctly", }; } @@ -186,13 +237,13 @@ const validatePattern = async (pattern, allowedFieldNames) => { if (!fieldsAreAllowed) { return { valid: false, - message: 'Pattern contains forbidden fields', + message: "Pattern contains forbidden fields", }; } return { valid: true, - message: 'Valid pattern', + message: "Valid pattern", }; }; diff --git a/server/utils/index.js b/server/utils/index.js index a75e77f..0cfeb21 100644 --- a/server/utils/index.js +++ b/server/utils/index.js @@ -1,25 +1,29 @@ -'use strict'; +"use strict"; const getCoreStore = () => { - return strapi.store({ type: 'plugin', name: 'sitemap' }); + return strapi.store({ type: "plugin", name: "sitemap" }); }; const getService = (name) => { - return strapi.plugin('sitemap').service(name); + return strapi.plugin("sitemap").service(name); }; -const logMessage = (msg = '') => `[strapi-plugin-sitemap]: ${msg}`; +const logMessage = (msg = "") => `[strapi-plugin-sitemap]: ${msg}`; const noLimit = async (strapi, queryString, parameters, limit = 5000) => { + console.log("[noLimit]", queryString, parameters); let entries = []; - const amountOfEntries = await strapi.entityService.count(queryString, parameters); + const amountOfEntries = await strapi.entityService.count( + queryString, + parameters + ); - for (let i = 0; i < (amountOfEntries / limit); i++) { + for (let i = 0; i < amountOfEntries / limit; i++) { /* eslint-disable-next-line */ const chunk = await strapi.entityService.findMany(queryString, { ...parameters, limit: limit, - start: (i * limit), + start: i * limit, }); if (chunk.id) { entries = [chunk, ...entries]; @@ -39,7 +43,9 @@ const formatCache = (cache, invalidationObject) => { Object.keys(invalidationObject).map((contentType) => { // Remove the items from the cache that will be refreshed. if (contentType && invalidationObject[contentType].ids) { - invalidationObject[contentType].ids.map((id) => delete cache[contentType]?.[id]); + invalidationObject[contentType].ids.map( + (id) => delete cache[contentType]?.[id] + ); } else if (contentType) { delete cache[contentType]; } @@ -47,10 +53,7 @@ const formatCache = (cache, invalidationObject) => { Object.values(cache).map((values) => { if (values) { - formattedCache = [ - ...formattedCache, - ...Object.values(values), - ]; + formattedCache = [...formattedCache, ...Object.values(values)]; } }); } From 4dec5b423ff504ded4eb3f569a66c10a910daa08 Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Mon, 9 Oct 2023 23:01:58 -0300 Subject: [PATCH 02/19] generating sitemap with many to many relationship --- server/services/core.js | 334 +++++++++++++++++++++++-------------- server/services/pattern.js | 52 +++--- server/services/query.js | 288 +++++++++++++++++++------------- 3 files changed, 411 insertions(+), 263 deletions(-) diff --git a/server/services/core.js b/server/services/core.js index 92ffc87..2a03a9c 100644 --- a/server/services/core.js +++ b/server/services/core.js @@ -1,14 +1,18 @@ -'use strict'; +"use strict"; /** * Sitemap service. */ -const { getConfigUrls } = require('@strapi/utils'); -const { SitemapStream, streamToPromise, SitemapAndIndexStream } = require('sitemap'); -const { isEmpty } = require('lodash'); +const { getConfigUrls } = require("@strapi/utils"); +const { + SitemapStream, + streamToPromise, + SitemapAndIndexStream, +} = require("sitemap"); +const { isEmpty } = require("lodash"); -const { logMessage, getService, formatCache, mergeCache } = require('../utils'); +const { logMessage, getService, formatCache, mergeCache } = require("../utils"); /** * Get a formatted array of different language URLs of a single page. @@ -26,31 +30,40 @@ const getLanguageLinks = async (config, page, contentType, defaultURL) => { const links = []; links.push({ lang: page.locale, url: defaultURL }); - await Promise.all(page.localizations.map(async (translation) => { - let { locale } = translation; - - // Return when there is no pattern for the page. - if ( - !config.contentTypes[contentType]['languages'][locale] - && config.contentTypes[contentType]['languages']['und'] - ) { - locale = 'und'; - } else if ( - !config.contentTypes[contentType]['languages'][locale] - && !config.contentTypes[contentType]['languages']['und'] - ) { - return null; - } + await Promise.all( + page.localizations.map(async (translation) => { + let { locale } = translation; + + // Return when there is no pattern for the page. + if ( + !config.contentTypes[contentType]["languages"][locale] && + config.contentTypes[contentType]["languages"]["und"] + ) { + locale = "und"; + } else if ( + !config.contentTypes[contentType]["languages"][locale] && + !config.contentTypes[contentType]["languages"]["und"] + ) { + return null; + } - const { pattern } = config.contentTypes[contentType]['languages'][locale]; - const translationUrl = await strapi.plugins.sitemap.services.pattern.resolvePattern(pattern, translation); - let hostnameOverride = config.hostname_overrides[translation.locale] || ''; - hostnameOverride = hostnameOverride.replace(/\/+$/, ''); - links.push({ - lang: translation.locale, - url: `${hostnameOverride}${translationUrl}`, - }); - })); + const { pattern } = config.contentTypes[contentType]["languages"][locale]; + console.log("[getLanguageLinks] pattern", pattern); + + const translationUrl = + await strapi.plugins.sitemap.services.pattern.resolvePattern( + pattern, + translation + ); + let hostnameOverride = + config.hostname_overrides[translation.locale] || ""; + hostnameOverride = hostnameOverride.replace(/\/+$/, ""); + links.push({ + lang: translation.locale, + url: `${hostnameOverride}${translationUrl}`, + }); + }) + ); return links; }; @@ -66,36 +79,65 @@ const getLanguageLinks = async (config, page, contentType, defaultURL) => { * @returns {object} The sitemap entry data. */ const getSitemapPageData = async (config, page, contentType) => { - let locale = page.locale || 'und'; + console.log( + "[getSitemapPageData] config", + config, + "page", + page, + "contentType", + contentType + ); + + console.log( + "[getSitemapPageData] config", + config.contentTypes["api::attraction.attraction"].languages + ); + let locale = page.locale || "und"; // Return when there is no pattern for the page. if ( - !config.contentTypes[contentType]['languages'][locale] - && config.contentTypes[contentType]['languages']['und'] + !config.contentTypes[contentType]["languages"][locale] && + config.contentTypes[contentType]["languages"]["und"] ) { - locale = 'und'; + locale = "und"; } else if ( - !config.contentTypes[contentType]['languages'][locale] - && !config.contentTypes[contentType]['languages']['und'] + !config.contentTypes[contentType]["languages"][locale] && + !config.contentTypes[contentType]["languages"]["und"] ) { return null; } - const { pattern } = config.contentTypes[contentType]['languages'][locale]; - const path = await strapi.plugins.sitemap.services.pattern.resolvePattern(pattern, page); - let hostnameOverride = config.hostname_overrides[page.locale] || ''; - hostnameOverride = hostnameOverride.replace(/\/+$/, ''); + const { pattern } = config.contentTypes[contentType]["languages"][locale]; + console.log("[getSitemapPageData] pattern", pattern); + + const path = await strapi.plugins.sitemap.services.pattern.resolvePattern( + pattern, + page + ); + + console.log("[getSitemapPageData] path", path); + + let hostnameOverride = config.hostname_overrides[page.locale] || ""; + hostnameOverride = hostnameOverride.replace(/\/+$/, ""); const url = `${hostnameOverride}${path}`; const pageData = { lastmod: page.updatedAt, url: url, links: await getLanguageLinks(config, page, contentType, url), - changefreq: config.contentTypes[contentType]['languages'][locale].changefreq || 'monthly', - priority: parseFloat(config.contentTypes[contentType]['languages'][locale].priority) || 0.5, + changefreq: + config.contentTypes[contentType]["languages"][locale].changefreq || + "monthly", + priority: + parseFloat( + config.contentTypes[contentType]["languages"][locale].priority + ) || 0.5, }; - if (config.contentTypes[contentType]['languages'][locale].includeLastmod === false) { + if ( + config.contentTypes[contentType]["languages"][locale].includeLastmod === + false + ) { delete pageData.lastmod; } @@ -110,53 +152,68 @@ const getSitemapPageData = async (config, page, contentType) => { * @returns {object} The cache and regular entries. */ const createSitemapEntries = async (invalidationObject) => { - const config = await getService('settings').getConfig(); + const config = await getService("settings").getConfig(); const sitemapEntries = []; const cacheEntries = {}; // Collection entries. - await Promise.all(Object.keys(config.contentTypes).map(async (contentType) => { - if (invalidationObject && !Object.keys(invalidationObject).includes(contentType)) { - return; - } - - cacheEntries[contentType] = {}; - - // Query all the pages - const pages = await getService('query').getPages(config, contentType, invalidationObject?.[contentType]?.ids); - - // Add formatted sitemap page data to the array. - await Promise.all(pages.map(async (page, i) => { - const pageData = await getSitemapPageData(config, page, contentType); - if (pageData) { - sitemapEntries.push(pageData); - - // Add page to the cache. - cacheEntries[contentType][page.id] = pageData; + await Promise.all( + Object.keys(config.contentTypes).map(async (contentType) => { + if ( + invalidationObject && + !Object.keys(invalidationObject).includes(contentType) + ) { + return; } - })); - - })); + cacheEntries[contentType] = {}; + + // Query all the pages + const pages = await getService("query").getPages( + config, + contentType, + invalidationObject?.[contentType]?.ids + ); + + console.log("[createSitemapEntries] pages", pages); + + // Add formatted sitemap page data to the array. + await Promise.all( + pages.map(async (page, i) => { + const pageData = await getSitemapPageData(config, page, contentType); + if (pageData) { + sitemapEntries.push(pageData); + + // Add page to the cache. + cacheEntries[contentType][page.id] = pageData; + } + }) + ); + }) + ); // Custom entries. - await Promise.all(Object.keys(config.customEntries).map(async (customEntry) => { - sitemapEntries.push({ - url: customEntry, - changefreq: config.customEntries[customEntry].changefreq, - priority: parseFloat(config.customEntries[customEntry].priority), - }); - })); + await Promise.all( + Object.keys(config.customEntries).map(async (customEntry) => { + sitemapEntries.push({ + url: customEntry, + changefreq: config.customEntries[customEntry].changefreq, + priority: parseFloat(config.customEntries[customEntry].priority), + }); + }) + ); // Custom homepage entry. if (config.includeHomepage) { - const hasHomePage = !isEmpty(sitemapEntries.filter((entry) => entry.url === '')); + const hasHomePage = !isEmpty( + sitemapEntries.filter((entry) => entry.url === "") + ); // Only add it when no other '/' entry is present. if (!hasHomePage) { sitemapEntries.push({ - url: '/', - changefreq: 'monthly', + url: "/", + changefreq: "monthly", priority: 1, }); } @@ -178,19 +235,27 @@ const saveSitemap = async (filename, sitemap, isIndex) => { return streamToPromise(sitemap) .then(async (sm) => { try { - return await getService('query').createSitemap({ + return await getService("query").createSitemap({ sitemap_string: sm.toString(), - name: 'default', + name: "default", delta: 0, - type: isIndex ? 'index' : 'default_hreflang', + type: isIndex ? "index" : "default_hreflang", }); } catch (e) { - strapi.log.error(logMessage(`Something went wrong while trying to write the sitemap XML to the database. ${e}`)); + strapi.log.error( + logMessage( + `Something went wrong while trying to write the sitemap XML to the database. ${e}` + ) + ); throw new Error(); } }) .catch((err) => { - strapi.log.error(logMessage(`Something went wrong while trying to build the sitemap with streamToPromise. ${err}`)); + strapi.log.error( + logMessage( + `Something went wrong while trying to build the sitemap with streamToPromise. ${err}` + ) + ); throw new Error(); }); }; @@ -203,49 +268,56 @@ const saveSitemap = async (filename, sitemap, isIndex) => { * @returns {SitemapStream} - The sitemap stream. */ const getSitemapStream = async (urlCount) => { - const config = await getService('settings').getConfig(); - const LIMIT = strapi.config.get('plugin.sitemap.limit'); - const enableXsl = strapi.config.get('plugin.sitemap.xsl'); + const config = await getService("settings").getConfig(); + const LIMIT = strapi.config.get("plugin.sitemap.limit"); + const enableXsl = strapi.config.get("plugin.sitemap.xsl"); const { serverUrl } = getConfigUrls(strapi.config); const xslObj = {}; if (enableXsl) { - xslObj.xslUrl = 'xsl/sitemap.xsl'; + xslObj.xslUrl = "xsl/sitemap.xsl"; } if (urlCount <= LIMIT) { - return [new SitemapStream({ - hostname: config.hostname, - ...xslObj, - }), false]; + return [ + new SitemapStream({ + hostname: config.hostname, + ...xslObj, + }), + false, + ]; } else { + return [ + new SitemapAndIndexStream({ + limit: LIMIT, + ...xslObj, + lastmodDateOnly: false, + getSitemapStream: (i) => { + const sitemapStream = new SitemapStream({ + hostname: config.hostname, + ...xslObj, + }); + const delta = i + 1; + const path = `api/sitemap/index.xml?page=${delta}`; - return [new SitemapAndIndexStream({ - limit: LIMIT, - ...xslObj, - lastmodDateOnly: false, - getSitemapStream: (i) => { - const sitemapStream = new SitemapStream({ - hostname: config.hostname, - ...xslObj, - }); - const delta = i + 1; - const path = `api/sitemap/index.xml?page=${delta}`; - - streamToPromise(sitemapStream) - .then((sm) => { - getService('query').createSitemap({ + streamToPromise(sitemapStream).then((sm) => { + getService("query").createSitemap({ sitemap_string: sm.toString(), - name: 'default', - type: 'default_hreflang', + name: "default", + type: "default_hreflang", delta, }); }); - return [new URL(path, serverUrl || 'http://localhost:1337').toString(), sitemapStream]; - }, - }), true]; + return [ + new URL(path, serverUrl || "http://localhost:1337").toString(), + sitemapStream, + ]; + }, + }), + true, + ]; } }; @@ -258,48 +330,62 @@ const getSitemapStream = async (urlCount) => { * @returns {void} */ const createSitemap = async (cache, invalidationObject) => { - const cachingEnabled = strapi.config.get('plugin.sitemap.caching'); - const autoGenerationEnabled = strapi.config.get('plugin.sitemap.autoGenerate'); + const cachingEnabled = strapi.config.get("plugin.sitemap.caching"); + const autoGenerationEnabled = strapi.config.get( + "plugin.sitemap.autoGenerate" + ); try { - const { - sitemapEntries, - cacheEntries, - } = await createSitemapEntries(invalidationObject); + const { sitemapEntries, cacheEntries } = await createSitemapEntries( + invalidationObject + ); // Format cache to regular entries const formattedCache = formatCache(cache, invalidationObject); - const allEntries = [ - ...sitemapEntries, - ...formattedCache, - ]; + const allEntries = [...sitemapEntries, ...formattedCache]; if (isEmpty(allEntries)) { - strapi.log.info(logMessage('No sitemap XML was generated because there were 0 URLs configured.')); + strapi.log.info( + logMessage( + "No sitemap XML was generated because there were 0 URLs configured." + ) + ); return; } - await getService('query').deleteSitemap('default'); + await getService("query").deleteSitemap("default"); const [sitemap, isIndex] = await getSitemapStream(allEntries.length); allEntries.map((sitemapEntry) => sitemap.write(sitemapEntry)); sitemap.end(); - const sitemapId = await saveSitemap('default', sitemap, isIndex); + const sitemapId = await saveSitemap("default", sitemap, isIndex); if (cachingEnabled && autoGenerationEnabled) { if (!cache) { - getService('query').createSitemapCache(cacheEntries, 'default', sitemapId); + getService("query").createSitemapCache( + cacheEntries, + "default", + sitemapId + ); } else { const newCache = mergeCache(cache, cacheEntries); - getService('query').updateSitemapCache(newCache, 'default', sitemapId); + getService("query").updateSitemapCache(newCache, "default", sitemapId); } } - strapi.log.info(logMessage('The sitemap XML has been generated. It can be accessed on /api/sitemap/index.xml.')); + strapi.log.info( + logMessage( + "The sitemap XML has been generated. It can be accessed on /api/sitemap/index.xml." + ) + ); } catch (err) { - strapi.log.error(logMessage(`Something went wrong while trying to build the SitemapStream. ${err}`)); + strapi.log.error( + logMessage( + `Something went wrong while trying to build the SitemapStream. ${err}` + ) + ); throw new Error(); } }; diff --git a/server/services/pattern.js b/server/services/pattern.js index 712b972..f6bb6a6 100644 --- a/server/services/pattern.js +++ b/server/services/pattern.js @@ -31,7 +31,7 @@ const getAllowedFields = (contentType, allowedFields = []) => { } else if ( field.type === "relation" && field.target && - field.relation.endsWith("ToOne") && // TODO: implement `ToMany` relations (#78). + field.relation.endsWith("ToOne") && fieldName !== "localizations" && fieldName !== "createdBy" && fieldName !== "updatedBy" @@ -58,10 +58,6 @@ const getAllowedFields = (contentType, allowedFields = []) => { ) { const relation = strapi.contentTypes[field.target]; - // if (fieldTypes.includes("id") && !fields.includes(`${fieldName}.id`)) { - // fields.push(`${fieldName}[0].id`); - // } - Object.entries(relation.attributes).map(([subFieldName, subField]) => { if (subField.type === fieldType || subFieldName === fieldType) { fields.push(`${fieldName}[0].${subFieldName}`); @@ -105,22 +101,12 @@ const getAllowedFields = (contentType, allowedFields = []) => { * @returns {array} The fields. */ const getFieldsFromPattern = (pattern, topLevel = false, relation = null) => { - console.log( - "[getFieldsFromPattern] pattern", - pattern, - "topLevel", - topLevel, - "relation", - relation - ); + console.log("[getFieldsFromPattern] pattern", pattern); let fields = pattern.match(/(?<=\/)(\[.*?\])(?=\/|$)/g); // Get all substrings between [] as array. // PR - Now it works with [value[0].field] - console.log("[getFieldsFromPattern] fields 1", fields); - // eslint-disable-next-line prefer-regex-literals fields = fields.map((field) => field.replace(/^.|.$/g, "")); // Strip [] from string. // PR - Simplify regex - console.log("[getFieldsFromPattern] fields 2", fields); if (relation) { fields = fields.filter( @@ -132,6 +118,8 @@ const getFieldsFromPattern = (pattern, topLevel = false, relation = null) => { fields = fields.filter((field) => field.split(".").length === 1); } + console.log("[getFieldsFromPattern] fields", fields); + return fields; }; @@ -144,8 +132,14 @@ const getFieldsFromPattern = (pattern, topLevel = false, relation = null) => { */ const getRelationsFromPattern = (pattern) => { let fields = getFieldsFromPattern(pattern); + + console.log("[getRelationsFromPattern] fields", fields); + fields = fields.filter((field) => field.split(".").length > 1); // Filter on fields containing a dot (.) - fields = fields.map((field) => field.split(".")[0]); // Extract the first part of the fields + fields = fields + .map((field) => field.split(".")[0]) // Extract the first part of the fields. Ex: categories[0].slug -> categories[0] + .map((field) => field.split("[")[0]); // Extract the first part of the fields. Ex: categories[0] -> categories + return fields; }; @@ -162,14 +156,24 @@ const resolvePattern = async (pattern, entity) => { const fields = getFieldsFromPattern(pattern); fields.map((field) => { - const relationalField = - field.split(".").length > 1 ? field.split(".") : null; + let relationalField = field.split(".").length > 1 ? field.split(".") : null; + + if (field && field.includes("[")) { + // If the relational field many to many + const childField = field.split("[")[0]; + relationalField = [childField, relationalField[1]]; + } if (!relationalField) { pattern = pattern.replace(`[${field}]`, entity[field] || ""); } else if (Array.isArray(entity[relationalField[0]])) { - strapi.log.error( - logMessage("Something went wrong whilst resolving the pattern.") + pattern = pattern.replace( + `[${field}]`, + entity[relationalField[0]] && + entity[relationalField[0]][0] && + entity[relationalField[0]][0][relationalField[1]] + ? entity[relationalField[0]][0][relationalField[1]] + : "" ); } else if (typeof entity[relationalField[0]] === "object") { pattern = pattern.replace( @@ -198,12 +202,6 @@ const resolvePattern = async (pattern, entity) => { * @returns {string} object.message Validation string. */ const validatePattern = async (pattern, allowedFieldNames) => { - console.log( - "[validatePattern] pattern", - pattern, - "allowedFieldNames", - allowedFieldNames - ); if (!pattern) { return { valid: false, diff --git a/server/services/query.js b/server/services/query.js index 4196c89..3b83f2a 100644 --- a/server/services/query.js +++ b/server/services/query.js @@ -1,13 +1,13 @@ /* eslint-disable camelcase */ -'use strict'; +"use strict"; -const { get } = require('lodash'); -const xml2js = require('xml2js'); +const { get } = require("lodash"); +const xml2js = require("xml2js"); -const parser = new xml2js.Parser({ attrkey: 'ATTR' }); +const parser = new xml2js.Parser({ attrkey: "ATTR" }); -const { noLimit, getService, logMessage } = require('../utils'); +const { noLimit, getService, logMessage } = require("../utils"); /** * Query service. @@ -24,21 +24,32 @@ const { noLimit, getService, logMessage } = require('../utils'); * * @returns {array} The fields. */ -const getFieldsFromConfig = (contentType, topLevel = false, isLocalized = false, relation = null) => { +const getFieldsFromConfig = ( + contentType, + topLevel = false, + isLocalized = false, + relation = null +) => { let fields = []; if (contentType) { - Object.entries(contentType['languages']).map(([langcode, { pattern }]) => { - fields.push(...getService('pattern').getFieldsFromPattern(pattern, topLevel, relation)); + Object.entries(contentType["languages"]).map(([langcode, { pattern }]) => { + fields.push( + ...getService("pattern").getFieldsFromPattern( + pattern, + topLevel, + relation + ) + ); }); } if (topLevel) { if (isLocalized) { - fields.push('locale'); + fields.push("locale"); } - fields.push('updatedAt'); + fields.push("updatedAt"); } // Remove duplicates @@ -59,8 +70,9 @@ const getRelationsFromConfig = (contentType) => { const relationsObject = {}; if (contentType) { - Object.entries(contentType['languages']).map(([langcode, { pattern }]) => { - const relations = getService('pattern').getRelationsFromPattern(pattern); + Object.entries(contentType["languages"]).map(([langcode, { pattern }]) => { + const relations = getService("pattern").getRelationsFromPattern(pattern); + console.log("[getRelationsFromConfig] relations", relations); relations.map((relation) => { relationsObject[relation] = { fields: getFieldsFromConfig(contentType, false, false, relation), @@ -69,6 +81,8 @@ const getRelationsFromConfig = (contentType) => { }); } + console.log("[getRelationsFromConfig] relationsObject", relationsObject); + return relationsObject; }; @@ -82,11 +96,22 @@ const getRelationsFromConfig = (contentType) => { * @returns {object} The pages. */ const getPages = async (config, contentType, ids) => { - const excludeDrafts = config.excludeDrafts && strapi.contentTypes[contentType].options.draftAndPublish; - const isLocalized = strapi.contentTypes[contentType].pluginOptions?.i18n?.localized; + const excludeDrafts = + config.excludeDrafts && + strapi.contentTypes[contentType].options.draftAndPublish; + const isLocalized = + strapi.contentTypes[contentType].pluginOptions?.i18n?.localized; const relations = getRelationsFromConfig(config.contentTypes[contentType]); - const fields = getFieldsFromConfig(config.contentTypes[contentType], true, isLocalized); + console.log("[getPages] relations", relations); + + const fields = getFieldsFromConfig( + config.contentTypes[contentType], + true, + isLocalized + ); + + console.log("[getPages] fields", fields); const pages = await noLimit(strapi, contentType, { filters: { @@ -102,11 +127,13 @@ const getPages = async (config, contentType, ids) => { }, }, ], - id: ids ? { - $in: ids, - } : {}, + id: ids + ? { + $in: ids, + } + : {}, }, - locale: 'all', + locale: "all", fields, populate: { localizations: { @@ -115,8 +142,8 @@ const getPages = async (config, contentType, ids) => { }, ...relations, }, - orderBy: 'id', - publicationState: excludeDrafts ? 'live' : 'preview', + orderBy: "id", + publicationState: excludeDrafts ? "live" : "preview", }); return pages; @@ -131,14 +158,15 @@ const getPages = async (config, contentType, ids) => { * @returns {object} The pages. */ const getLocalizationIds = async (contentType, ids) => { - const isLocalized = strapi.contentTypes[contentType].pluginOptions?.i18n?.localized; + const isLocalized = + strapi.contentTypes[contentType].pluginOptions?.i18n?.localized; const localizationIds = []; if (isLocalized) { const response = await strapi.entityService.findMany(contentType, { filters: { localizations: ids }, - locale: 'all', - fields: ['id'], + locale: "all", + fields: ["id"], }); response.map((localization) => localizationIds.push(localization.id)); @@ -157,13 +185,18 @@ const getLocalizationIds = async (contentType, ids) => { * * @returns {object} The invalidation object. */ -const composeInvalidationObject = async (config, type, queryFilters, ids = []) => { +const composeInvalidationObject = async ( + config, + type, + queryFilters, + ids = [] +) => { const mainIds = [...ids]; if (ids.length === 0) { const updatedIds = await strapi.entityService.findMany(type, { filters: queryFilters, - fields: ['id'], + fields: ["id"], }); updatedIds.map((page) => mainIds.push(page.id)); } @@ -173,42 +206,50 @@ const composeInvalidationObject = async (config, type, queryFilters, ids = []) = // Add the updated entity. const invalidationObject = { [type]: { - ids: [ - ...mainLocaleIds, - ...mainIds, - ], + ids: [...mainLocaleIds, ...mainIds], }, }; // Add all pages that have a relation to the updated entity. - await Promise.all(Object.keys(config.contentTypes).map(async (contentType) => { - const relations = Object.keys(getRelationsFromConfig(config.contentTypes[contentType])); - - await Promise.all(relations.map(async (relation) => { - if (strapi.contentTypes[contentType].attributes[relation].target === type) { - - const pagesToUpdate = await strapi.entityService.findMany(contentType, { - filters: { [relation]: mainIds }, - fields: ['id'], - }); - - if (pagesToUpdate.length > 0 && !invalidationObject[contentType]) { - invalidationObject[contentType] = {}; - } - - const relatedIds = []; - pagesToUpdate.map((page) => relatedIds.push(page.id)); - const relatedLocaleIds = await getLocalizationIds(contentType, relatedIds); - - invalidationObject[contentType] = { - ids: [ - ...relatedLocaleIds, - ...relatedIds, - ], - }; - } - })); - })); + await Promise.all( + Object.keys(config.contentTypes).map(async (contentType) => { + const relations = Object.keys( + getRelationsFromConfig(config.contentTypes[contentType]) + ); + + await Promise.all( + relations.map(async (relation) => { + if ( + strapi.contentTypes[contentType].attributes[relation].target === + type + ) { + const pagesToUpdate = await strapi.entityService.findMany( + contentType, + { + filters: { [relation]: mainIds }, + fields: ["id"], + } + ); + + if (pagesToUpdate.length > 0 && !invalidationObject[contentType]) { + invalidationObject[contentType] = {}; + } + + const relatedIds = []; + pagesToUpdate.map((page) => relatedIds.push(page.id)); + const relatedLocaleIds = await getLocalizationIds( + contentType, + relatedIds + ); + + invalidationObject[contentType] = { + ids: [...relatedLocaleIds, ...relatedIds], + }; + } + }) + ); + }) + ); return invalidationObject; }; @@ -222,14 +263,17 @@ const composeInvalidationObject = async (config, type, queryFilters, ids = []) = * * @returns {void} */ -const getSitemap = async (name, delta, fields = ['sitemap_string']) => { - const sitemap = await strapi.entityService.findMany('plugin::sitemap.sitemap', { - filters: { - name, - delta, - }, - fields, - }); +const getSitemap = async (name, delta, fields = ["sitemap_string"]) => { + const sitemap = await strapi.entityService.findMany( + "plugin::sitemap.sitemap", + { + filters: { + name, + delta, + }, + fields, + } + ); return sitemap[0]; }; @@ -242,16 +286,21 @@ const getSitemap = async (name, delta, fields = ['sitemap_string']) => { * @returns {void} */ const deleteSitemap = async (name) => { - const sitemaps = await strapi.entityService.findMany('plugin::sitemap.sitemap', { - filters: { - name, - }, - fields: ['id'], - }); + const sitemaps = await strapi.entityService.findMany( + "plugin::sitemap.sitemap", + { + filters: { + name, + }, + fields: ["id"], + } + ); - await Promise.all(sitemaps.map(async (sm) => { - await strapi.entityService.delete('plugin::sitemap.sitemap', sm.id); - })); + await Promise.all( + sitemaps.map(async (sm) => { + await strapi.entityService.delete("plugin::sitemap.sitemap", sm.id); + }) + ); }; /** @@ -262,27 +311,26 @@ const deleteSitemap = async (name) => { * @returns {void} */ const createSitemap = async (data) => { - const { - name, - delta, - type, - sitemap_string, - } = data; + const { name, delta, type, sitemap_string } = data; let linkCount = null; parser.parseString(sitemap_string, (error, result) => { if (error) { - strapi.log.error(logMessage(`An error occurred while trying to parse the sitemap XML to json. ${error}`)); + strapi.log.error( + logMessage( + `An error occurred while trying to parse the sitemap XML to json. ${error}` + ) + ); throw new Error(); - } else if (type === 'index') { - linkCount = get(result, 'sitemapindex.sitemap.length') || 0; + } else if (type === "index") { + linkCount = get(result, "sitemapindex.sitemap.length") || 0; } else { - linkCount = get(result, 'urlset.url.length') || 0; + linkCount = get(result, "urlset.url.length") || 0; } }); - const sitemap = await strapi.entityService.create('plugin::sitemap.sitemap', { + const sitemap = await strapi.entityService.create("plugin::sitemap.sitemap", { data: { sitemap_string, name, @@ -305,18 +353,24 @@ const createSitemap = async (data) => { * @returns {void} */ const createSitemapCache = async (sitemapJson, name, sitemapId) => { - const sitemap = await strapi.entityService.findMany('plugin::sitemap.sitemap-cache', { - filters: { - name, - }, - fields: ['id'], - }); + const sitemap = await strapi.entityService.findMany( + "plugin::sitemap.sitemap-cache", + { + filters: { + name, + }, + fields: ["id"], + } + ); if (sitemap[0]) { - await strapi.entityService.delete('plugin::sitemap.sitemap-cache', sitemap[0].id); + await strapi.entityService.delete( + "plugin::sitemap.sitemap-cache", + sitemap[0].id + ); } - await strapi.entityService.create('plugin::sitemap.sitemap-cache', { + await strapi.entityService.create("plugin::sitemap.sitemap-cache", { data: { sitemap_json: sitemapJson, sitemap_id: sitemapId, @@ -335,21 +389,28 @@ const createSitemapCache = async (sitemapJson, name, sitemapId) => { * @returns {void} */ const updateSitemapCache = async (sitemapJson, name, sitemapId) => { - const sitemap = await strapi.entityService.findMany('plugin::sitemap.sitemap-cache', { - filters: { - name, - }, - fields: ['id'], - }); - - if (sitemap[0]) { - await strapi.entityService.update('plugin::sitemap.sitemap-cache', sitemap[0].id, { - data: { - sitemap_json: sitemapJson, - sitemap_id: sitemapId, + const sitemap = await strapi.entityService.findMany( + "plugin::sitemap.sitemap-cache", + { + filters: { name, }, - }); + fields: ["id"], + } + ); + + if (sitemap[0]) { + await strapi.entityService.update( + "plugin::sitemap.sitemap-cache", + sitemap[0].id, + { + data: { + sitemap_json: sitemapJson, + sitemap_id: sitemapId, + name, + }, + } + ); } }; @@ -361,11 +422,14 @@ const updateSitemapCache = async (sitemapJson, name, sitemapId) => { * @returns {void} */ const getSitemapCache = async (name) => { - const sitemap = await strapi.entityService.findMany('plugin::sitemap.sitemap-cache', { - filters: { - name, - }, - }); + const sitemap = await strapi.entityService.findMany( + "plugin::sitemap.sitemap-cache", + { + filters: { + name, + }, + } + ); return sitemap[0]; }; From f7e51fedc0da71a11a003f8649b487c5218e65da Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Mon, 9 Oct 2023 23:03:23 -0300 Subject: [PATCH 03/19] added comment --- server/services/pattern.js | 1 + 1 file changed, 1 insertion(+) diff --git a/server/services/pattern.js b/server/services/pattern.js index f6bb6a6..7e5c2bf 100644 --- a/server/services/pattern.js +++ b/server/services/pattern.js @@ -167,6 +167,7 @@ const resolvePattern = async (pattern, entity) => { if (!relationalField) { pattern = pattern.replace(`[${field}]`, entity[field] || ""); } else if (Array.isArray(entity[relationalField[0]])) { + // Many to Many relationship pattern = pattern.replace( `[${field}]`, entity[relationalField[0]] && From dbd6a11af834c0029dd68c63b20ced68fe7af3fd Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Mon, 9 Oct 2023 23:05:39 -0300 Subject: [PATCH 04/19] Remove console logs --- server/controllers/settings.js | 1 - server/services/core.js | 7 ------- server/services/pattern.js | 6 ------ server/services/query.js | 7 ------- server/utils/index.js | 1 - 5 files changed, 22 deletions(-) diff --git a/server/controllers/settings.js b/server/controllers/settings.js index b394510..4798177 100644 --- a/server/controllers/settings.js +++ b/server/controllers/settings.js @@ -21,7 +21,6 @@ module.exports = { (x) => !Object.keys(config.contentTypes).includes(x) ); - console.log("[updateSettings] newContentTypes", newContentTypes); await strapi .store({ environment: "", diff --git a/server/services/core.js b/server/services/core.js index 2a03a9c..da981a5 100644 --- a/server/services/core.js +++ b/server/services/core.js @@ -48,7 +48,6 @@ const getLanguageLinks = async (config, page, contentType, defaultURL) => { } const { pattern } = config.contentTypes[contentType]["languages"][locale]; - console.log("[getLanguageLinks] pattern", pattern); const translationUrl = await strapi.plugins.sitemap.services.pattern.resolvePattern( @@ -108,15 +107,11 @@ const getSitemapPageData = async (config, page, contentType) => { } const { pattern } = config.contentTypes[contentType]["languages"][locale]; - console.log("[getSitemapPageData] pattern", pattern); - const path = await strapi.plugins.sitemap.services.pattern.resolvePattern( pattern, page ); - console.log("[getSitemapPageData] path", path); - let hostnameOverride = config.hostname_overrides[page.locale] || ""; hostnameOverride = hostnameOverride.replace(/\/+$/, ""); const url = `${hostnameOverride}${path}`; @@ -175,8 +170,6 @@ const createSitemapEntries = async (invalidationObject) => { invalidationObject?.[contentType]?.ids ); - console.log("[createSitemapEntries] pages", pages); - // Add formatted sitemap page data to the array. await Promise.all( pages.map(async (page, i) => { diff --git a/server/services/pattern.js b/server/services/pattern.js index 7e5c2bf..0fd9c8e 100644 --- a/server/services/pattern.js +++ b/server/services/pattern.js @@ -101,8 +101,6 @@ const getAllowedFields = (contentType, allowedFields = []) => { * @returns {array} The fields. */ const getFieldsFromPattern = (pattern, topLevel = false, relation = null) => { - console.log("[getFieldsFromPattern] pattern", pattern); - let fields = pattern.match(/(?<=\/)(\[.*?\])(?=\/|$)/g); // Get all substrings between [] as array. // PR - Now it works with [value[0].field] // eslint-disable-next-line prefer-regex-literals @@ -118,8 +116,6 @@ const getFieldsFromPattern = (pattern, topLevel = false, relation = null) => { fields = fields.filter((field) => field.split(".").length === 1); } - console.log("[getFieldsFromPattern] fields", fields); - return fields; }; @@ -133,8 +129,6 @@ const getFieldsFromPattern = (pattern, topLevel = false, relation = null) => { const getRelationsFromPattern = (pattern) => { let fields = getFieldsFromPattern(pattern); - console.log("[getRelationsFromPattern] fields", fields); - fields = fields.filter((field) => field.split(".").length > 1); // Filter on fields containing a dot (.) fields = fields .map((field) => field.split(".")[0]) // Extract the first part of the fields. Ex: categories[0].slug -> categories[0] diff --git a/server/services/query.js b/server/services/query.js index 3b83f2a..2a9d56f 100644 --- a/server/services/query.js +++ b/server/services/query.js @@ -72,7 +72,6 @@ const getRelationsFromConfig = (contentType) => { if (contentType) { Object.entries(contentType["languages"]).map(([langcode, { pattern }]) => { const relations = getService("pattern").getRelationsFromPattern(pattern); - console.log("[getRelationsFromConfig] relations", relations); relations.map((relation) => { relationsObject[relation] = { fields: getFieldsFromConfig(contentType, false, false, relation), @@ -81,8 +80,6 @@ const getRelationsFromConfig = (contentType) => { }); } - console.log("[getRelationsFromConfig] relationsObject", relationsObject); - return relationsObject; }; @@ -103,16 +100,12 @@ const getPages = async (config, contentType, ids) => { strapi.contentTypes[contentType].pluginOptions?.i18n?.localized; const relations = getRelationsFromConfig(config.contentTypes[contentType]); - console.log("[getPages] relations", relations); - const fields = getFieldsFromConfig( config.contentTypes[contentType], true, isLocalized ); - console.log("[getPages] fields", fields); - const pages = await noLimit(strapi, contentType, { filters: { $or: [ diff --git a/server/utils/index.js b/server/utils/index.js index 0cfeb21..d4ec6a7 100644 --- a/server/utils/index.js +++ b/server/utils/index.js @@ -11,7 +11,6 @@ const getService = (name) => { const logMessage = (msg = "") => `[strapi-plugin-sitemap]: ${msg}`; const noLimit = async (strapi, queryString, parameters, limit = 5000) => { - console.log("[noLimit]", queryString, parameters); let entries = []; const amountOfEntries = await strapi.entityService.count( queryString, From 0c26f2d42615f9f95e816f39d8f2947b79b86c21 Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Mon, 9 Oct 2023 23:06:56 -0300 Subject: [PATCH 05/19] remove console.logs --- server/services/core.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/server/services/core.js b/server/services/core.js index da981a5..90f093b 100644 --- a/server/services/core.js +++ b/server/services/core.js @@ -78,19 +78,6 @@ const getLanguageLinks = async (config, page, contentType, defaultURL) => { * @returns {object} The sitemap entry data. */ const getSitemapPageData = async (config, page, contentType) => { - console.log( - "[getSitemapPageData] config", - config, - "page", - page, - "contentType", - contentType - ); - - console.log( - "[getSitemapPageData] config", - config.contentTypes["api::attraction.attraction"].languages - ); let locale = page.locale || "und"; // Return when there is no pattern for the page. From 6618a8ad40371ab410940cf83706b85babf4a5ce Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Mon, 9 Oct 2023 23:09:52 -0300 Subject: [PATCH 06/19] Remove comments --- server/services/pattern.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/services/pattern.js b/server/services/pattern.js index 0fd9c8e..587576a 100644 --- a/server/services/pattern.js +++ b/server/services/pattern.js @@ -22,7 +22,6 @@ const getAllowedFields = (contentType, allowedFields = []) => { : strapi.config.get("plugin.sitemap.allowedFields"); fieldTypes.map((fieldType) => { Object.entries(contentType.attributes).map(([fieldName, field]) => { - // console.warn("[getAllowedFields] fieldName", fieldName, "field", field); if ( (field.type === fieldType || fieldName === fieldType) && field.type !== "relation" @@ -101,10 +100,10 @@ const getAllowedFields = (contentType, allowedFields = []) => { * @returns {array} The fields. */ const getFieldsFromPattern = (pattern, topLevel = false, relation = null) => { - let fields = pattern.match(/(?<=\/)(\[.*?\])(?=\/|$)/g); // Get all substrings between [] as array. // PR - Now it works with [value[0].field] + let fields = pattern.match(/(?<=\/)(\[.*?\])(?=\/|$)/g); // Get all substrings between [] as array. // eslint-disable-next-line prefer-regex-literals - fields = fields.map((field) => field.replace(/^.|.$/g, "")); // Strip [] from string. // PR - Simplify regex + fields = fields.map((field) => field.replace(/^.|.$/g, "")); // Strip [] from string. if (relation) { fields = fields.filter( From 52a697d01508a1cf03dcfe168aa792b7445a6332 Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Mon, 9 Oct 2023 23:23:34 -0300 Subject: [PATCH 07/19] fix quotes --- .editorconfig | 1 + server/services/core.js | 102 ++++++++++++++++++------------------- server/services/pattern.js | 82 ++++++++++++++--------------- server/services/query.js | 70 ++++++++++++------------- server/utils/index.js | 8 +-- 5 files changed, 132 insertions(+), 131 deletions(-) diff --git a/.editorconfig b/.editorconfig index 8b1709b..db1db0f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,6 +7,7 @@ end_of_line = LF charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true +quote_type = single [*.md] trim_trailing_whitespace = false \ No newline at end of file diff --git a/server/services/core.js b/server/services/core.js index 90f093b..67995e5 100644 --- a/server/services/core.js +++ b/server/services/core.js @@ -1,18 +1,18 @@ -"use strict"; +'use strict'; /** * Sitemap service. */ -const { getConfigUrls } = require("@strapi/utils"); +const { getConfigUrls } = require('@strapi/utils'); const { SitemapStream, streamToPromise, SitemapAndIndexStream, -} = require("sitemap"); -const { isEmpty } = require("lodash"); +} = require('sitemap'); +const { isEmpty } = require('lodash'); -const { logMessage, getService, formatCache, mergeCache } = require("../utils"); +const { logMessage, getService, formatCache, mergeCache } = require('../utils'); /** * Get a formatted array of different language URLs of a single page. @@ -36,18 +36,18 @@ const getLanguageLinks = async (config, page, contentType, defaultURL) => { // Return when there is no pattern for the page. if ( - !config.contentTypes[contentType]["languages"][locale] && - config.contentTypes[contentType]["languages"]["und"] + !config.contentTypes[contentType]['languages'][locale] && + config.contentTypes[contentType]['languages']['und'] ) { - locale = "und"; + locale = 'und'; } else if ( - !config.contentTypes[contentType]["languages"][locale] && - !config.contentTypes[contentType]["languages"]["und"] + !config.contentTypes[contentType]['languages'][locale] && + !config.contentTypes[contentType]['languages']['und'] ) { return null; } - const { pattern } = config.contentTypes[contentType]["languages"][locale]; + const { pattern } = config.contentTypes[contentType]['languages'][locale]; const translationUrl = await strapi.plugins.sitemap.services.pattern.resolvePattern( @@ -55,8 +55,8 @@ const getLanguageLinks = async (config, page, contentType, defaultURL) => { translation ); let hostnameOverride = - config.hostname_overrides[translation.locale] || ""; - hostnameOverride = hostnameOverride.replace(/\/+$/, ""); + config.hostname_overrides[translation.locale] || ''; + hostnameOverride = hostnameOverride.replace(/\/+$/, ''); links.push({ lang: translation.locale, url: `${hostnameOverride}${translationUrl}`, @@ -78,29 +78,29 @@ const getLanguageLinks = async (config, page, contentType, defaultURL) => { * @returns {object} The sitemap entry data. */ const getSitemapPageData = async (config, page, contentType) => { - let locale = page.locale || "und"; + let locale = page.locale || 'und'; // Return when there is no pattern for the page. if ( - !config.contentTypes[contentType]["languages"][locale] && - config.contentTypes[contentType]["languages"]["und"] + !config.contentTypes[contentType]['languages'][locale] && + config.contentTypes[contentType]['languages']['und'] ) { - locale = "und"; + locale = 'und'; } else if ( - !config.contentTypes[contentType]["languages"][locale] && - !config.contentTypes[contentType]["languages"]["und"] + !config.contentTypes[contentType]['languages'][locale] && + !config.contentTypes[contentType]['languages']['und'] ) { return null; } - const { pattern } = config.contentTypes[contentType]["languages"][locale]; + const { pattern } = config.contentTypes[contentType]['languages'][locale]; const path = await strapi.plugins.sitemap.services.pattern.resolvePattern( pattern, page ); - let hostnameOverride = config.hostname_overrides[page.locale] || ""; - hostnameOverride = hostnameOverride.replace(/\/+$/, ""); + let hostnameOverride = config.hostname_overrides[page.locale] || ''; + hostnameOverride = hostnameOverride.replace(/\/+$/, ''); const url = `${hostnameOverride}${path}`; const pageData = { @@ -108,16 +108,16 @@ const getSitemapPageData = async (config, page, contentType) => { url: url, links: await getLanguageLinks(config, page, contentType, url), changefreq: - config.contentTypes[contentType]["languages"][locale].changefreq || - "monthly", + config.contentTypes[contentType]['languages'][locale].changefreq || + 'monthly', priority: parseFloat( - config.contentTypes[contentType]["languages"][locale].priority + config.contentTypes[contentType]['languages'][locale].priority ) || 0.5, }; if ( - config.contentTypes[contentType]["languages"][locale].includeLastmod === + config.contentTypes[contentType]['languages'][locale].includeLastmod === false ) { delete pageData.lastmod; @@ -134,7 +134,7 @@ const getSitemapPageData = async (config, page, contentType) => { * @returns {object} The cache and regular entries. */ const createSitemapEntries = async (invalidationObject) => { - const config = await getService("settings").getConfig(); + const config = await getService('settings').getConfig(); const sitemapEntries = []; const cacheEntries = {}; @@ -151,7 +151,7 @@ const createSitemapEntries = async (invalidationObject) => { cacheEntries[contentType] = {}; // Query all the pages - const pages = await getService("query").getPages( + const pages = await getService('query').getPages( config, contentType, invalidationObject?.[contentType]?.ids @@ -186,14 +186,14 @@ const createSitemapEntries = async (invalidationObject) => { // Custom homepage entry. if (config.includeHomepage) { const hasHomePage = !isEmpty( - sitemapEntries.filter((entry) => entry.url === "") + sitemapEntries.filter((entry) => entry.url === '') ); // Only add it when no other '/' entry is present. if (!hasHomePage) { sitemapEntries.push({ - url: "/", - changefreq: "monthly", + url: '/', + changefreq: 'monthly', priority: 1, }); } @@ -215,11 +215,11 @@ const saveSitemap = async (filename, sitemap, isIndex) => { return streamToPromise(sitemap) .then(async (sm) => { try { - return await getService("query").createSitemap({ + return await getService('query').createSitemap({ sitemap_string: sm.toString(), - name: "default", + name: 'default', delta: 0, - type: isIndex ? "index" : "default_hreflang", + type: isIndex ? 'index' : 'default_hreflang', }); } catch (e) { strapi.log.error( @@ -248,15 +248,15 @@ const saveSitemap = async (filename, sitemap, isIndex) => { * @returns {SitemapStream} - The sitemap stream. */ const getSitemapStream = async (urlCount) => { - const config = await getService("settings").getConfig(); - const LIMIT = strapi.config.get("plugin.sitemap.limit"); - const enableXsl = strapi.config.get("plugin.sitemap.xsl"); + const config = await getService('settings').getConfig(); + const LIMIT = strapi.config.get('plugin.sitemap.limit'); + const enableXsl = strapi.config.get('plugin.sitemap.xsl'); const { serverUrl } = getConfigUrls(strapi.config); const xslObj = {}; if (enableXsl) { - xslObj.xslUrl = "xsl/sitemap.xsl"; + xslObj.xslUrl = 'xsl/sitemap.xsl'; } if (urlCount <= LIMIT) { @@ -282,16 +282,16 @@ const getSitemapStream = async (urlCount) => { const path = `api/sitemap/index.xml?page=${delta}`; streamToPromise(sitemapStream).then((sm) => { - getService("query").createSitemap({ + getService('query').createSitemap({ sitemap_string: sm.toString(), - name: "default", - type: "default_hreflang", + name: 'default', + type: 'default_hreflang', delta, }); }); return [ - new URL(path, serverUrl || "http://localhost:1337").toString(), + new URL(path, serverUrl || 'http://localhost:1337').toString(), sitemapStream, ]; }, @@ -310,9 +310,9 @@ const getSitemapStream = async (urlCount) => { * @returns {void} */ const createSitemap = async (cache, invalidationObject) => { - const cachingEnabled = strapi.config.get("plugin.sitemap.caching"); + const cachingEnabled = strapi.config.get('plugin.sitemap.caching'); const autoGenerationEnabled = strapi.config.get( - "plugin.sitemap.autoGenerate" + 'plugin.sitemap.autoGenerate' ); try { @@ -327,37 +327,37 @@ const createSitemap = async (cache, invalidationObject) => { if (isEmpty(allEntries)) { strapi.log.info( logMessage( - "No sitemap XML was generated because there were 0 URLs configured." + 'No sitemap XML was generated because there were 0 URLs configured.' ) ); return; } - await getService("query").deleteSitemap("default"); + await getService('query').deleteSitemap('default'); const [sitemap, isIndex] = await getSitemapStream(allEntries.length); allEntries.map((sitemapEntry) => sitemap.write(sitemapEntry)); sitemap.end(); - const sitemapId = await saveSitemap("default", sitemap, isIndex); + const sitemapId = await saveSitemap('default', sitemap, isIndex); if (cachingEnabled && autoGenerationEnabled) { if (!cache) { - getService("query").createSitemapCache( + getService('query').createSitemapCache( cacheEntries, - "default", + 'default', sitemapId ); } else { const newCache = mergeCache(cache, cacheEntries); - getService("query").updateSitemapCache(newCache, "default", sitemapId); + getService('query').updateSitemapCache(newCache, 'default', sitemapId); } } strapi.log.info( logMessage( - "The sitemap XML has been generated. It can be accessed on /api/sitemap/index.xml." + 'The sitemap XML has been generated. It can be accessed on /api/sitemap/index.xml.' ) ); } catch (err) { diff --git a/server/services/pattern.js b/server/services/pattern.js index 587576a..f5ce091 100644 --- a/server/services/pattern.js +++ b/server/services/pattern.js @@ -1,6 +1,6 @@ -"use strict"; +'use strict'; -const { logMessage } = require("../utils"); +const { logMessage } = require('../utils'); /** * Pattern service. @@ -19,25 +19,25 @@ const getAllowedFields = (contentType, allowedFields = []) => { const fieldTypes = allowedFields.length > 0 ? allowedFields - : strapi.config.get("plugin.sitemap.allowedFields"); + : strapi.config.get('plugin.sitemap.allowedFields'); fieldTypes.map((fieldType) => { Object.entries(contentType.attributes).map(([fieldName, field]) => { if ( (field.type === fieldType || fieldName === fieldType) && - field.type !== "relation" + field.type !== 'relation' ) { fields.push(fieldName); } else if ( - field.type === "relation" && + field.type === 'relation' && field.target && - field.relation.endsWith("ToOne") && - fieldName !== "localizations" && - fieldName !== "createdBy" && - fieldName !== "updatedBy" + field.relation.endsWith('ToOne') && + fieldName !== 'localizations' && + fieldName !== 'createdBy' && + fieldName !== 'updatedBy' ) { const relation = strapi.contentTypes[field.target]; - if (fieldTypes.includes("id") && !fields.includes(`${fieldName}.id`)) { + if (fieldTypes.includes('id') && !fields.includes(`${fieldName}.id`)) { fields.push(`${fieldName}.id`); } @@ -47,13 +47,13 @@ const getAllowedFields = (contentType, allowedFields = []) => { } }); } else if ( - field.type === "relation" && + field.type === 'relation' && field.target && field.mappedBy && - field.relation.endsWith("ToMany") && - fieldName !== "localizations" && - fieldName !== "createdBy" && - fieldName !== "updatedBy" + field.relation.endsWith('ToMany') && + fieldName !== 'localizations' && + fieldName !== 'createdBy' && + fieldName !== 'updatedBy' ) { const relation = strapi.contentTypes[field.target]; @@ -63,13 +63,13 @@ const getAllowedFields = (contentType, allowedFields = []) => { } }); } else if ( - field.type === "component" && + field.type === 'component' && field.component && field.repeatable !== true // TODO: implement repeatable components (#78). ) { const relation = strapi.components[field.component]; - if (fieldTypes.includes("id") && !fields.includes(`${fieldName}.id`)) { + if (fieldTypes.includes('id') && !fields.includes(`${fieldName}.id`)) { fields.push(`${fieldName}.id`); } @@ -83,8 +83,8 @@ const getAllowedFields = (contentType, allowedFields = []) => { }); // Add id field manually because it is not on the attributes object of a content type. - if (fieldTypes.includes("id")) { - fields.push("id"); + if (fieldTypes.includes('id')) { + fields.push('id'); } return fields; @@ -103,16 +103,16 @@ const getFieldsFromPattern = (pattern, topLevel = false, relation = null) => { let fields = pattern.match(/(?<=\/)(\[.*?\])(?=\/|$)/g); // Get all substrings between [] as array. // eslint-disable-next-line prefer-regex-literals - fields = fields.map((field) => field.replace(/^.|.$/g, "")); // Strip [] from string. + fields = fields.map((field) => field.replace(/^.|.$/g, '')); // Strip [] from string. if (relation) { fields = fields.filter( (field) => field.startsWith(`${relation}.`) || field.startsWith(`${relation}[0].`) ); - fields = fields.map((field) => field.split(".")[1]); + fields = fields.map((field) => field.split('.')[1]); } else if (topLevel) { - fields = fields.filter((field) => field.split(".").length === 1); + fields = fields.filter((field) => field.split('.').length === 1); } return fields; @@ -128,10 +128,10 @@ const getFieldsFromPattern = (pattern, topLevel = false, relation = null) => { const getRelationsFromPattern = (pattern) => { let fields = getFieldsFromPattern(pattern); - fields = fields.filter((field) => field.split(".").length > 1); // Filter on fields containing a dot (.) + fields = fields.filter((field) => field.split('.').length > 1); // Filter on fields containing a dot (.) fields = fields - .map((field) => field.split(".")[0]) // Extract the first part of the fields. Ex: categories[0].slug -> categories[0] - .map((field) => field.split("[")[0]); // Extract the first part of the fields. Ex: categories[0] -> categories + .map((field) => field.split('.')[0]) // Extract the first part of the fields. Ex: categories[0].slug -> categories[0] + .map((field) => field.split('[')[0]); // Extract the first part of the fields. Ex: categories[0] -> categories return fields; }; @@ -149,16 +149,16 @@ const resolvePattern = async (pattern, entity) => { const fields = getFieldsFromPattern(pattern); fields.map((field) => { - let relationalField = field.split(".").length > 1 ? field.split(".") : null; + let relationalField = field.split('.').length > 1 ? field.split('.') : null; - if (field && field.includes("[")) { + if (field && field.includes('[')) { // If the relational field many to many - const childField = field.split("[")[0]; + const childField = field.split('[')[0]; relationalField = [childField, relationalField[1]]; } if (!relationalField) { - pattern = pattern.replace(`[${field}]`, entity[field] || ""); + pattern = pattern.replace(`[${field}]`, entity[field] || ''); } else if (Array.isArray(entity[relationalField[0]])) { // Many to Many relationship pattern = pattern.replace( @@ -167,21 +167,21 @@ const resolvePattern = async (pattern, entity) => { entity[relationalField[0]][0] && entity[relationalField[0]][0][relationalField[1]] ? entity[relationalField[0]][0][relationalField[1]] - : "" + : '' ); - } else if (typeof entity[relationalField[0]] === "object") { + } else if (typeof entity[relationalField[0]] === 'object') { pattern = pattern.replace( `[${field}]`, entity[relationalField[0]] && entity[relationalField[0]][relationalField[1]] ? entity[relationalField[0]][relationalField[1]] - : "" + : '' ); } }); - pattern = pattern.replace(/\/+/g, "/"); // Remove duplicate forward slashes. - pattern = pattern.startsWith("/") ? pattern : `/${pattern}`; // Make sure we only have on forward slash. + pattern = pattern.replace(/\/+/g, '/'); // Remove duplicate forward slashes. + pattern = pattern.startsWith('/') ? pattern : `/${pattern}`; // Make sure we only have on forward slash. return pattern; }; @@ -199,24 +199,24 @@ const validatePattern = async (pattern, allowedFieldNames) => { if (!pattern) { return { valid: false, - message: "Pattern can not be empty", + message: 'Pattern can not be empty', }; } - const preCharCount = pattern.split("[").length - 1; - const postCharount = pattern.split("]").length - 1; + const preCharCount = pattern.split('[').length - 1; + const postCharount = pattern.split(']').length - 1; if (preCharCount < 1 || postCharount < 1) { return { valid: false, - message: "Pattern should contain at least one field", + message: 'Pattern should contain at least one field', }; } if (preCharCount !== postCharount) { return { valid: false, - message: "Fields in the pattern are not escaped correctly", + message: 'Fields in the pattern are not escaped correctly', }; } @@ -229,13 +229,13 @@ const validatePattern = async (pattern, allowedFieldNames) => { if (!fieldsAreAllowed) { return { valid: false, - message: "Pattern contains forbidden fields", + message: 'Pattern contains forbidden fields', }; } return { valid: true, - message: "Valid pattern", + message: 'Valid pattern', }; }; diff --git a/server/services/query.js b/server/services/query.js index 2a9d56f..93891ed 100644 --- a/server/services/query.js +++ b/server/services/query.js @@ -1,13 +1,13 @@ /* eslint-disable camelcase */ -"use strict"; +'use strict'; -const { get } = require("lodash"); -const xml2js = require("xml2js"); +const { get } = require('lodash'); +const xml2js = require('xml2js'); -const parser = new xml2js.Parser({ attrkey: "ATTR" }); +const parser = new xml2js.Parser({ attrkey: 'ATTR' }); -const { noLimit, getService, logMessage } = require("../utils"); +const { noLimit, getService, logMessage } = require('../utils'); /** * Query service. @@ -33,9 +33,9 @@ const getFieldsFromConfig = ( let fields = []; if (contentType) { - Object.entries(contentType["languages"]).map(([langcode, { pattern }]) => { + Object.entries(contentType['languages']).map(([langcode, { pattern }]) => { fields.push( - ...getService("pattern").getFieldsFromPattern( + ...getService('pattern').getFieldsFromPattern( pattern, topLevel, relation @@ -46,10 +46,10 @@ const getFieldsFromConfig = ( if (topLevel) { if (isLocalized) { - fields.push("locale"); + fields.push('locale'); } - fields.push("updatedAt"); + fields.push('updatedAt'); } // Remove duplicates @@ -70,8 +70,8 @@ const getRelationsFromConfig = (contentType) => { const relationsObject = {}; if (contentType) { - Object.entries(contentType["languages"]).map(([langcode, { pattern }]) => { - const relations = getService("pattern").getRelationsFromPattern(pattern); + Object.entries(contentType['languages']).map(([langcode, { pattern }]) => { + const relations = getService('pattern').getRelationsFromPattern(pattern); relations.map((relation) => { relationsObject[relation] = { fields: getFieldsFromConfig(contentType, false, false, relation), @@ -126,7 +126,7 @@ const getPages = async (config, contentType, ids) => { } : {}, }, - locale: "all", + locale: 'all', fields, populate: { localizations: { @@ -135,8 +135,8 @@ const getPages = async (config, contentType, ids) => { }, ...relations, }, - orderBy: "id", - publicationState: excludeDrafts ? "live" : "preview", + orderBy: 'id', + publicationState: excludeDrafts ? 'live' : 'preview', }); return pages; @@ -158,8 +158,8 @@ const getLocalizationIds = async (contentType, ids) => { if (isLocalized) { const response = await strapi.entityService.findMany(contentType, { filters: { localizations: ids }, - locale: "all", - fields: ["id"], + locale: 'all', + fields: ['id'], }); response.map((localization) => localizationIds.push(localization.id)); @@ -189,7 +189,7 @@ const composeInvalidationObject = async ( if (ids.length === 0) { const updatedIds = await strapi.entityService.findMany(type, { filters: queryFilters, - fields: ["id"], + fields: ['id'], }); updatedIds.map((page) => mainIds.push(page.id)); } @@ -220,7 +220,7 @@ const composeInvalidationObject = async ( contentType, { filters: { [relation]: mainIds }, - fields: ["id"], + fields: ['id'], } ); @@ -256,9 +256,9 @@ const composeInvalidationObject = async ( * * @returns {void} */ -const getSitemap = async (name, delta, fields = ["sitemap_string"]) => { +const getSitemap = async (name, delta, fields = ['sitemap_string']) => { const sitemap = await strapi.entityService.findMany( - "plugin::sitemap.sitemap", + 'plugin::sitemap.sitemap', { filters: { name, @@ -280,18 +280,18 @@ const getSitemap = async (name, delta, fields = ["sitemap_string"]) => { */ const deleteSitemap = async (name) => { const sitemaps = await strapi.entityService.findMany( - "plugin::sitemap.sitemap", + 'plugin::sitemap.sitemap', { filters: { name, }, - fields: ["id"], + fields: ['id'], } ); await Promise.all( sitemaps.map(async (sm) => { - await strapi.entityService.delete("plugin::sitemap.sitemap", sm.id); + await strapi.entityService.delete('plugin::sitemap.sitemap', sm.id); }) ); }; @@ -316,14 +316,14 @@ const createSitemap = async (data) => { ) ); throw new Error(); - } else if (type === "index") { - linkCount = get(result, "sitemapindex.sitemap.length") || 0; + } else if (type === 'index') { + linkCount = get(result, 'sitemapindex.sitemap.length') || 0; } else { - linkCount = get(result, "urlset.url.length") || 0; + linkCount = get(result, 'urlset.url.length') || 0; } }); - const sitemap = await strapi.entityService.create("plugin::sitemap.sitemap", { + const sitemap = await strapi.entityService.create('plugin::sitemap.sitemap', { data: { sitemap_string, name, @@ -347,23 +347,23 @@ const createSitemap = async (data) => { */ const createSitemapCache = async (sitemapJson, name, sitemapId) => { const sitemap = await strapi.entityService.findMany( - "plugin::sitemap.sitemap-cache", + 'plugin::sitemap.sitemap-cache', { filters: { name, }, - fields: ["id"], + fields: ['id'], } ); if (sitemap[0]) { await strapi.entityService.delete( - "plugin::sitemap.sitemap-cache", + 'plugin::sitemap.sitemap-cache', sitemap[0].id ); } - await strapi.entityService.create("plugin::sitemap.sitemap-cache", { + await strapi.entityService.create('plugin::sitemap.sitemap-cache', { data: { sitemap_json: sitemapJson, sitemap_id: sitemapId, @@ -383,18 +383,18 @@ const createSitemapCache = async (sitemapJson, name, sitemapId) => { */ const updateSitemapCache = async (sitemapJson, name, sitemapId) => { const sitemap = await strapi.entityService.findMany( - "plugin::sitemap.sitemap-cache", + 'plugin::sitemap.sitemap-cache', { filters: { name, }, - fields: ["id"], + fields: ['id'], } ); if (sitemap[0]) { await strapi.entityService.update( - "plugin::sitemap.sitemap-cache", + 'plugin::sitemap.sitemap-cache', sitemap[0].id, { data: { @@ -416,7 +416,7 @@ const updateSitemapCache = async (sitemapJson, name, sitemapId) => { */ const getSitemapCache = async (name) => { const sitemap = await strapi.entityService.findMany( - "plugin::sitemap.sitemap-cache", + 'plugin::sitemap.sitemap-cache', { filters: { name, diff --git a/server/utils/index.js b/server/utils/index.js index d4ec6a7..025f9a9 100644 --- a/server/utils/index.js +++ b/server/utils/index.js @@ -1,14 +1,14 @@ -"use strict"; +'use strict'; const getCoreStore = () => { - return strapi.store({ type: "plugin", name: "sitemap" }); + return strapi.store({ type: 'plugin', name: 'sitemap' }); }; const getService = (name) => { - return strapi.plugin("sitemap").service(name); + return strapi.plugin('sitemap').service(name); }; -const logMessage = (msg = "") => `[strapi-plugin-sitemap]: ${msg}`; +const logMessage = (msg = '') => `[strapi-plugin-sitemap]: ${msg}`; const noLimit = async (strapi, queryString, parameters, limit = 5000) => { let entries = []; From 8aa49a464822588c96ea27bf3cf967a1505ae935 Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Mon, 9 Oct 2023 23:24:10 -0300 Subject: [PATCH 08/19] fix quotes --- server/controllers/settings.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/server/controllers/settings.js b/server/controllers/settings.js index 4798177..3973d35 100644 --- a/server/controllers/settings.js +++ b/server/controllers/settings.js @@ -1,6 +1,6 @@ -"use strict"; +'use strict'; -const { getService } = require("../utils"); +const { getService } = require('../utils'); /** * Sitemap.js controller @@ -10,28 +10,28 @@ const { getService } = require("../utils"); module.exports = { getSettings: async (ctx) => { - const config = await getService("settings").getConfig(); + const config = await getService('settings').getConfig(); ctx.send(config); }, updateSettings: async (ctx) => { - const config = await getService("settings").getConfig(); + const config = await getService('settings').getConfig(); const newContentTypes = Object.keys(ctx.request.body.contentTypes).filter( (x) => !Object.keys(config.contentTypes).includes(x) ); await strapi .store({ - environment: "", - type: "plugin", - name: "sitemap", + environment: '', + type: 'plugin', + name: 'sitemap', }) - .set({ key: "settings", value: ctx.request.body }); + .set({ key: 'settings', value: ctx.request.body }); // Load lifecycle methods for auto generation of sitemap. await newContentTypes.map(async (contentType) => { - await getService("lifecycle").loadLifecycleMethod(contentType); + await getService('lifecycle').loadLifecycleMethod(contentType); }); ctx.send({ ok: true }); From 91065d26d468cf81901d4125dc9236dd996eb941 Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Tue, 10 Oct 2023 06:59:28 -0300 Subject: [PATCH 09/19] getting the specific value from array --- .prettierrc.json | 3 +++ server/services/pattern.js | 32 +++++++++++++++++++++++--------- 2 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 .prettierrc.json diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..bf357fb --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,3 @@ +{ + "trailingComma": "all" +} diff --git a/server/services/pattern.js b/server/services/pattern.js index f5ce091..926fa31 100644 --- a/server/services/pattern.js +++ b/server/services/pattern.js @@ -108,7 +108,7 @@ const getFieldsFromPattern = (pattern, topLevel = false, relation = null) => { if (relation) { fields = fields.filter( (field) => - field.startsWith(`${relation}.`) || field.startsWith(`${relation}[0].`) + field.startsWith(`${relation}.`) || field.startsWith(`${relation}[`), ); fields = fields.map((field) => field.split('.')[1]); } else if (topLevel) { @@ -152,9 +152,14 @@ const resolvePattern = async (pattern, entity) => { let relationalField = field.split('.').length > 1 ? field.split('.') : null; if (field && field.includes('[')) { - // If the relational field many to many + // If the relational field many to many. Ex: categories[0].slug const childField = field.split('[')[0]; - relationalField = [childField, relationalField[1]]; + + // Extract array index + const indexExecArray = /\[(\d+)\]/g.exec(field); + const childIndexField = parseInt(indexExecArray[1], 10); + + relationalField = [childField, relationalField[1], childIndexField]; // ['categories', 'slug', 0] } if (!relationalField) { @@ -164,10 +169,10 @@ const resolvePattern = async (pattern, entity) => { pattern = pattern.replace( `[${field}]`, entity[relationalField[0]] && - entity[relationalField[0]][0] && - entity[relationalField[0]][0][relationalField[1]] - ? entity[relationalField[0]][0][relationalField[1]] - : '' + entity[relationalField[0]][relationalField[2]] && + entity[relationalField[0]][relationalField[2]][relationalField[1]] + ? entity[relationalField[0]][relationalField[2]][relationalField[1]] + : '', ); } else if (typeof entity[relationalField[0]] === 'object') { pattern = pattern.replace( @@ -175,7 +180,7 @@ const resolvePattern = async (pattern, entity) => { entity[relationalField[0]] && entity[relationalField[0]][relationalField[1]] ? entity[relationalField[0]][relationalField[1]] - : '' + : '', ); } }); @@ -223,7 +228,16 @@ const validatePattern = async (pattern, allowedFieldNames) => { let fieldsAreAllowed = true; getFieldsFromPattern(pattern).map((field) => { - if (!allowedFieldNames.includes(field)) fieldsAreAllowed = false; + if (field.includes('[')) { + // Validate value with array. Ex: categories[10].slug + const fieldReplaced = field.replace(/\[(\d+)\]/, '[0]'); + + if (!allowedFieldNames.includes(fieldReplaced)) { + fieldsAreAllowed = false; + } + } else if (!allowedFieldNames.includes(field)) { + fieldsAreAllowed = false; + } }); if (!fieldsAreAllowed) { From da356c29797b646bc2b65a04b2891174e19bfc50 Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Tue, 10 Oct 2023 07:02:44 -0300 Subject: [PATCH 10/19] fix lint issues --- server/services/core.js | 44 ++++++++++++++++++++-------------------- server/services/query.js | 40 ++++++++++++++++++------------------ server/utils/index.js | 4 ++-- 3 files changed, 44 insertions(+), 44 deletions(-) diff --git a/server/services/core.js b/server/services/core.js index 67995e5..ad6adb4 100644 --- a/server/services/core.js +++ b/server/services/core.js @@ -52,7 +52,7 @@ const getLanguageLinks = async (config, page, contentType, defaultURL) => { const translationUrl = await strapi.plugins.sitemap.services.pattern.resolvePattern( pattern, - translation + translation, ); let hostnameOverride = config.hostname_overrides[translation.locale] || ''; @@ -61,7 +61,7 @@ const getLanguageLinks = async (config, page, contentType, defaultURL) => { lang: translation.locale, url: `${hostnameOverride}${translationUrl}`, }); - }) + }), ); return links; @@ -96,7 +96,7 @@ const getSitemapPageData = async (config, page, contentType) => { const { pattern } = config.contentTypes[contentType]['languages'][locale]; const path = await strapi.plugins.sitemap.services.pattern.resolvePattern( pattern, - page + page, ); let hostnameOverride = config.hostname_overrides[page.locale] || ''; @@ -112,7 +112,7 @@ const getSitemapPageData = async (config, page, contentType) => { 'monthly', priority: parseFloat( - config.contentTypes[contentType]['languages'][locale].priority + config.contentTypes[contentType]['languages'][locale].priority, ) || 0.5, }; @@ -154,7 +154,7 @@ const createSitemapEntries = async (invalidationObject) => { const pages = await getService('query').getPages( config, contentType, - invalidationObject?.[contentType]?.ids + invalidationObject?.[contentType]?.ids, ); // Add formatted sitemap page data to the array. @@ -167,9 +167,9 @@ const createSitemapEntries = async (invalidationObject) => { // Add page to the cache. cacheEntries[contentType][page.id] = pageData; } - }) + }), ); - }) + }), ); // Custom entries. @@ -180,13 +180,13 @@ const createSitemapEntries = async (invalidationObject) => { changefreq: config.customEntries[customEntry].changefreq, priority: parseFloat(config.customEntries[customEntry].priority), }); - }) + }), ); // Custom homepage entry. if (config.includeHomepage) { const hasHomePage = !isEmpty( - sitemapEntries.filter((entry) => entry.url === '') + sitemapEntries.filter((entry) => entry.url === ''), ); // Only add it when no other '/' entry is present. @@ -224,8 +224,8 @@ const saveSitemap = async (filename, sitemap, isIndex) => { } catch (e) { strapi.log.error( logMessage( - `Something went wrong while trying to write the sitemap XML to the database. ${e}` - ) + `Something went wrong while trying to write the sitemap XML to the database. ${e}`, + ), ); throw new Error(); } @@ -233,8 +233,8 @@ const saveSitemap = async (filename, sitemap, isIndex) => { .catch((err) => { strapi.log.error( logMessage( - `Something went wrong while trying to build the sitemap with streamToPromise. ${err}` - ) + `Something went wrong while trying to build the sitemap with streamToPromise. ${err}`, + ), ); throw new Error(); }); @@ -312,12 +312,12 @@ const getSitemapStream = async (urlCount) => { const createSitemap = async (cache, invalidationObject) => { const cachingEnabled = strapi.config.get('plugin.sitemap.caching'); const autoGenerationEnabled = strapi.config.get( - 'plugin.sitemap.autoGenerate' + 'plugin.sitemap.autoGenerate', ); try { const { sitemapEntries, cacheEntries } = await createSitemapEntries( - invalidationObject + invalidationObject, ); // Format cache to regular entries const formattedCache = formatCache(cache, invalidationObject); @@ -327,8 +327,8 @@ const createSitemap = async (cache, invalidationObject) => { if (isEmpty(allEntries)) { strapi.log.info( logMessage( - 'No sitemap XML was generated because there were 0 URLs configured.' - ) + 'No sitemap XML was generated because there were 0 URLs configured.', + ), ); return; } @@ -347,7 +347,7 @@ const createSitemap = async (cache, invalidationObject) => { getService('query').createSitemapCache( cacheEntries, 'default', - sitemapId + sitemapId, ); } else { const newCache = mergeCache(cache, cacheEntries); @@ -357,14 +357,14 @@ const createSitemap = async (cache, invalidationObject) => { strapi.log.info( logMessage( - 'The sitemap XML has been generated. It can be accessed on /api/sitemap/index.xml.' - ) + 'The sitemap XML has been generated. It can be accessed on /api/sitemap/index.xml.', + ), ); } catch (err) { strapi.log.error( logMessage( - `Something went wrong while trying to build the SitemapStream. ${err}` - ) + `Something went wrong while trying to build the SitemapStream. ${err}`, + ), ); throw new Error(); } diff --git a/server/services/query.js b/server/services/query.js index 93891ed..ff7b925 100644 --- a/server/services/query.js +++ b/server/services/query.js @@ -28,7 +28,7 @@ const getFieldsFromConfig = ( contentType, topLevel = false, isLocalized = false, - relation = null + relation = null, ) => { let fields = []; @@ -38,8 +38,8 @@ const getFieldsFromConfig = ( ...getService('pattern').getFieldsFromPattern( pattern, topLevel, - relation - ) + relation, + ), ); }); } @@ -103,7 +103,7 @@ const getPages = async (config, contentType, ids) => { const fields = getFieldsFromConfig( config.contentTypes[contentType], true, - isLocalized + isLocalized, ); const pages = await noLimit(strapi, contentType, { @@ -182,7 +182,7 @@ const composeInvalidationObject = async ( config, type, queryFilters, - ids = [] + ids = [], ) => { const mainIds = [...ids]; @@ -207,7 +207,7 @@ const composeInvalidationObject = async ( await Promise.all( Object.keys(config.contentTypes).map(async (contentType) => { const relations = Object.keys( - getRelationsFromConfig(config.contentTypes[contentType]) + getRelationsFromConfig(config.contentTypes[contentType]), ); await Promise.all( @@ -221,7 +221,7 @@ const composeInvalidationObject = async ( { filters: { [relation]: mainIds }, fields: ['id'], - } + }, ); if (pagesToUpdate.length > 0 && !invalidationObject[contentType]) { @@ -232,16 +232,16 @@ const composeInvalidationObject = async ( pagesToUpdate.map((page) => relatedIds.push(page.id)); const relatedLocaleIds = await getLocalizationIds( contentType, - relatedIds + relatedIds, ); invalidationObject[contentType] = { ids: [...relatedLocaleIds, ...relatedIds], }; } - }) + }), ); - }) + }), ); return invalidationObject; @@ -265,7 +265,7 @@ const getSitemap = async (name, delta, fields = ['sitemap_string']) => { delta, }, fields, - } + }, ); return sitemap[0]; @@ -286,13 +286,13 @@ const deleteSitemap = async (name) => { name, }, fields: ['id'], - } + }, ); await Promise.all( sitemaps.map(async (sm) => { await strapi.entityService.delete('plugin::sitemap.sitemap', sm.id); - }) + }), ); }; @@ -312,8 +312,8 @@ const createSitemap = async (data) => { if (error) { strapi.log.error( logMessage( - `An error occurred while trying to parse the sitemap XML to json. ${error}` - ) + `An error occurred while trying to parse the sitemap XML to json. ${error}`, + ), ); throw new Error(); } else if (type === 'index') { @@ -353,13 +353,13 @@ const createSitemapCache = async (sitemapJson, name, sitemapId) => { name, }, fields: ['id'], - } + }, ); if (sitemap[0]) { await strapi.entityService.delete( 'plugin::sitemap.sitemap-cache', - sitemap[0].id + sitemap[0].id, ); } @@ -389,7 +389,7 @@ const updateSitemapCache = async (sitemapJson, name, sitemapId) => { name, }, fields: ['id'], - } + }, ); if (sitemap[0]) { @@ -402,7 +402,7 @@ const updateSitemapCache = async (sitemapJson, name, sitemapId) => { sitemap_id: sitemapId, name, }, - } + }, ); } }; @@ -421,7 +421,7 @@ const getSitemapCache = async (name) => { filters: { name, }, - } + }, ); return sitemap[0]; diff --git a/server/utils/index.js b/server/utils/index.js index 025f9a9..41fb8a3 100644 --- a/server/utils/index.js +++ b/server/utils/index.js @@ -14,7 +14,7 @@ const noLimit = async (strapi, queryString, parameters, limit = 5000) => { let entries = []; const amountOfEntries = await strapi.entityService.count( queryString, - parameters + parameters, ); for (let i = 0; i < amountOfEntries / limit; i++) { @@ -43,7 +43,7 @@ const formatCache = (cache, invalidationObject) => { // Remove the items from the cache that will be refreshed. if (contentType && invalidationObject[contentType].ids) { invalidationObject[contentType].ids.map( - (id) => delete cache[contentType]?.[id] + (id) => delete cache[contentType]?.[id], ); } else if (contentType) { delete cache[contentType]; From 80e1874999acb79eb6b8e1ff4b35edf845b145ad Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Tue, 10 Oct 2023 07:18:25 -0300 Subject: [PATCH 11/19] fix lint --- .prettierrc.json | 3 ++- server/controllers/settings.js | 2 +- server/services/pattern.js | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.prettierrc.json b/.prettierrc.json index bf357fb..12618fa 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,3 +1,4 @@ { - "trailingComma": "all" + "trailingComma": "all", + "arrowParens": "always" } diff --git a/server/controllers/settings.js b/server/controllers/settings.js index 3973d35..7c9e2aa 100644 --- a/server/controllers/settings.js +++ b/server/controllers/settings.js @@ -18,7 +18,7 @@ module.exports = { updateSettings: async (ctx) => { const config = await getService('settings').getConfig(); const newContentTypes = Object.keys(ctx.request.body.contentTypes).filter( - (x) => !Object.keys(config.contentTypes).includes(x) + (x) => !Object.keys(config.contentTypes).includes(x), ); await strapi diff --git a/server/services/pattern.js b/server/services/pattern.js index 926fa31..1126ec0 100644 --- a/server/services/pattern.js +++ b/server/services/pattern.js @@ -1,7 +1,5 @@ 'use strict'; -const { logMessage } = require('../utils'); - /** * Pattern service. */ @@ -108,6 +106,7 @@ const getFieldsFromPattern = (pattern, topLevel = false, relation = null) => { if (relation) { fields = fields.filter( (field) => + // eslint-disable-next-line implicit-arrow-linebreak field.startsWith(`${relation}.`) || field.startsWith(`${relation}[`), ); fields = fields.map((field) => field.split('.')[1]); From 716f7c99d4d83d428ff36f03c29ec665a64b78ef Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Tue, 17 Oct 2023 21:21:00 -0400 Subject: [PATCH 12/19] fix lint --- server/services/query.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/server/services/query.js b/server/services/query.js index ff7b925..09a18ec 100644 --- a/server/services/query.js +++ b/server/services/query.js @@ -120,11 +120,7 @@ const getPages = async (config, contentType, ids) => { }, }, ], - id: ids - ? { - $in: ids, - } - : {}, + id: ids ? { $in: ids } : {}, }, locale: 'all', fields, From 6ae3f6e89db5e63d689967652326413cf8d819ca Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Tue, 17 Oct 2023 21:25:53 -0400 Subject: [PATCH 13/19] revert core.js --- server/services/core.js | 262 +++++++++++++++------------------------- 1 file changed, 98 insertions(+), 164 deletions(-) diff --git a/server/services/core.js b/server/services/core.js index ad6adb4..92ffc87 100644 --- a/server/services/core.js +++ b/server/services/core.js @@ -5,11 +5,7 @@ */ const { getConfigUrls } = require('@strapi/utils'); -const { - SitemapStream, - streamToPromise, - SitemapAndIndexStream, -} = require('sitemap'); +const { SitemapStream, streamToPromise, SitemapAndIndexStream } = require('sitemap'); const { isEmpty } = require('lodash'); const { logMessage, getService, formatCache, mergeCache } = require('../utils'); @@ -30,39 +26,31 @@ const getLanguageLinks = async (config, page, contentType, defaultURL) => { const links = []; links.push({ lang: page.locale, url: defaultURL }); - await Promise.all( - page.localizations.map(async (translation) => { - let { locale } = translation; - - // Return when there is no pattern for the page. - if ( - !config.contentTypes[contentType]['languages'][locale] && - config.contentTypes[contentType]['languages']['und'] - ) { - locale = 'und'; - } else if ( - !config.contentTypes[contentType]['languages'][locale] && - !config.contentTypes[contentType]['languages']['und'] - ) { - return null; - } + await Promise.all(page.localizations.map(async (translation) => { + let { locale } = translation; + + // Return when there is no pattern for the page. + if ( + !config.contentTypes[contentType]['languages'][locale] + && config.contentTypes[contentType]['languages']['und'] + ) { + locale = 'und'; + } else if ( + !config.contentTypes[contentType]['languages'][locale] + && !config.contentTypes[contentType]['languages']['und'] + ) { + return null; + } - const { pattern } = config.contentTypes[contentType]['languages'][locale]; - - const translationUrl = - await strapi.plugins.sitemap.services.pattern.resolvePattern( - pattern, - translation, - ); - let hostnameOverride = - config.hostname_overrides[translation.locale] || ''; - hostnameOverride = hostnameOverride.replace(/\/+$/, ''); - links.push({ - lang: translation.locale, - url: `${hostnameOverride}${translationUrl}`, - }); - }), - ); + const { pattern } = config.contentTypes[contentType]['languages'][locale]; + const translationUrl = await strapi.plugins.sitemap.services.pattern.resolvePattern(pattern, translation); + let hostnameOverride = config.hostname_overrides[translation.locale] || ''; + hostnameOverride = hostnameOverride.replace(/\/+$/, ''); + links.push({ + lang: translation.locale, + url: `${hostnameOverride}${translationUrl}`, + }); + })); return links; }; @@ -82,23 +70,19 @@ const getSitemapPageData = async (config, page, contentType) => { // Return when there is no pattern for the page. if ( - !config.contentTypes[contentType]['languages'][locale] && - config.contentTypes[contentType]['languages']['und'] + !config.contentTypes[contentType]['languages'][locale] + && config.contentTypes[contentType]['languages']['und'] ) { locale = 'und'; } else if ( - !config.contentTypes[contentType]['languages'][locale] && - !config.contentTypes[contentType]['languages']['und'] + !config.contentTypes[contentType]['languages'][locale] + && !config.contentTypes[contentType]['languages']['und'] ) { return null; } const { pattern } = config.contentTypes[contentType]['languages'][locale]; - const path = await strapi.plugins.sitemap.services.pattern.resolvePattern( - pattern, - page, - ); - + const path = await strapi.plugins.sitemap.services.pattern.resolvePattern(pattern, page); let hostnameOverride = config.hostname_overrides[page.locale] || ''; hostnameOverride = hostnameOverride.replace(/\/+$/, ''); const url = `${hostnameOverride}${path}`; @@ -107,19 +91,11 @@ const getSitemapPageData = async (config, page, contentType) => { lastmod: page.updatedAt, url: url, links: await getLanguageLinks(config, page, contentType, url), - changefreq: - config.contentTypes[contentType]['languages'][locale].changefreq || - 'monthly', - priority: - parseFloat( - config.contentTypes[contentType]['languages'][locale].priority, - ) || 0.5, + changefreq: config.contentTypes[contentType]['languages'][locale].changefreq || 'monthly', + priority: parseFloat(config.contentTypes[contentType]['languages'][locale].priority) || 0.5, }; - if ( - config.contentTypes[contentType]['languages'][locale].includeLastmod === - false - ) { + if (config.contentTypes[contentType]['languages'][locale].includeLastmod === false) { delete pageData.lastmod; } @@ -139,55 +115,42 @@ const createSitemapEntries = async (invalidationObject) => { const cacheEntries = {}; // Collection entries. - await Promise.all( - Object.keys(config.contentTypes).map(async (contentType) => { - if ( - invalidationObject && - !Object.keys(invalidationObject).includes(contentType) - ) { - return; + await Promise.all(Object.keys(config.contentTypes).map(async (contentType) => { + if (invalidationObject && !Object.keys(invalidationObject).includes(contentType)) { + return; + } + + cacheEntries[contentType] = {}; + + // Query all the pages + const pages = await getService('query').getPages(config, contentType, invalidationObject?.[contentType]?.ids); + + // Add formatted sitemap page data to the array. + await Promise.all(pages.map(async (page, i) => { + const pageData = await getSitemapPageData(config, page, contentType); + if (pageData) { + sitemapEntries.push(pageData); + + // Add page to the cache. + cacheEntries[contentType][page.id] = pageData; } + })); + + })); - cacheEntries[contentType] = {}; - - // Query all the pages - const pages = await getService('query').getPages( - config, - contentType, - invalidationObject?.[contentType]?.ids, - ); - - // Add formatted sitemap page data to the array. - await Promise.all( - pages.map(async (page, i) => { - const pageData = await getSitemapPageData(config, page, contentType); - if (pageData) { - sitemapEntries.push(pageData); - - // Add page to the cache. - cacheEntries[contentType][page.id] = pageData; - } - }), - ); - }), - ); // Custom entries. - await Promise.all( - Object.keys(config.customEntries).map(async (customEntry) => { - sitemapEntries.push({ - url: customEntry, - changefreq: config.customEntries[customEntry].changefreq, - priority: parseFloat(config.customEntries[customEntry].priority), - }); - }), - ); + await Promise.all(Object.keys(config.customEntries).map(async (customEntry) => { + sitemapEntries.push({ + url: customEntry, + changefreq: config.customEntries[customEntry].changefreq, + priority: parseFloat(config.customEntries[customEntry].priority), + }); + })); // Custom homepage entry. if (config.includeHomepage) { - const hasHomePage = !isEmpty( - sitemapEntries.filter((entry) => entry.url === ''), - ); + const hasHomePage = !isEmpty(sitemapEntries.filter((entry) => entry.url === '')); // Only add it when no other '/' entry is present. if (!hasHomePage) { @@ -222,20 +185,12 @@ const saveSitemap = async (filename, sitemap, isIndex) => { type: isIndex ? 'index' : 'default_hreflang', }); } catch (e) { - strapi.log.error( - logMessage( - `Something went wrong while trying to write the sitemap XML to the database. ${e}`, - ), - ); + strapi.log.error(logMessage(`Something went wrong while trying to write the sitemap XML to the database. ${e}`)); throw new Error(); } }) .catch((err) => { - strapi.log.error( - logMessage( - `Something went wrong while trying to build the sitemap with streamToPromise. ${err}`, - ), - ); + strapi.log.error(logMessage(`Something went wrong while trying to build the sitemap with streamToPromise. ${err}`)); throw new Error(); }); }; @@ -260,28 +215,26 @@ const getSitemapStream = async (urlCount) => { } if (urlCount <= LIMIT) { - return [ - new SitemapStream({ - hostname: config.hostname, - ...xslObj, - }), - false, - ]; + return [new SitemapStream({ + hostname: config.hostname, + ...xslObj, + }), false]; } else { - return [ - new SitemapAndIndexStream({ - limit: LIMIT, - ...xslObj, - lastmodDateOnly: false, - getSitemapStream: (i) => { - const sitemapStream = new SitemapStream({ - hostname: config.hostname, - ...xslObj, - }); - const delta = i + 1; - const path = `api/sitemap/index.xml?page=${delta}`; - streamToPromise(sitemapStream).then((sm) => { + return [new SitemapAndIndexStream({ + limit: LIMIT, + ...xslObj, + lastmodDateOnly: false, + getSitemapStream: (i) => { + const sitemapStream = new SitemapStream({ + hostname: config.hostname, + ...xslObj, + }); + const delta = i + 1; + const path = `api/sitemap/index.xml?page=${delta}`; + + streamToPromise(sitemapStream) + .then((sm) => { getService('query').createSitemap({ sitemap_string: sm.toString(), name: 'default', @@ -290,14 +243,9 @@ const getSitemapStream = async (urlCount) => { }); }); - return [ - new URL(path, serverUrl || 'http://localhost:1337').toString(), - sitemapStream, - ]; - }, - }), - true, - ]; + return [new URL(path, serverUrl || 'http://localhost:1337').toString(), sitemapStream]; + }, + }), true]; } }; @@ -311,25 +259,23 @@ const getSitemapStream = async (urlCount) => { */ const createSitemap = async (cache, invalidationObject) => { const cachingEnabled = strapi.config.get('plugin.sitemap.caching'); - const autoGenerationEnabled = strapi.config.get( - 'plugin.sitemap.autoGenerate', - ); + const autoGenerationEnabled = strapi.config.get('plugin.sitemap.autoGenerate'); try { - const { sitemapEntries, cacheEntries } = await createSitemapEntries( - invalidationObject, - ); + const { + sitemapEntries, + cacheEntries, + } = await createSitemapEntries(invalidationObject); // Format cache to regular entries const formattedCache = formatCache(cache, invalidationObject); - const allEntries = [...sitemapEntries, ...formattedCache]; + const allEntries = [ + ...sitemapEntries, + ...formattedCache, + ]; if (isEmpty(allEntries)) { - strapi.log.info( - logMessage( - 'No sitemap XML was generated because there were 0 URLs configured.', - ), - ); + strapi.log.info(logMessage('No sitemap XML was generated because there were 0 URLs configured.')); return; } @@ -344,28 +290,16 @@ const createSitemap = async (cache, invalidationObject) => { if (cachingEnabled && autoGenerationEnabled) { if (!cache) { - getService('query').createSitemapCache( - cacheEntries, - 'default', - sitemapId, - ); + getService('query').createSitemapCache(cacheEntries, 'default', sitemapId); } else { const newCache = mergeCache(cache, cacheEntries); getService('query').updateSitemapCache(newCache, 'default', sitemapId); } } - strapi.log.info( - logMessage( - 'The sitemap XML has been generated. It can be accessed on /api/sitemap/index.xml.', - ), - ); + strapi.log.info(logMessage('The sitemap XML has been generated. It can be accessed on /api/sitemap/index.xml.')); } catch (err) { - strapi.log.error( - logMessage( - `Something went wrong while trying to build the SitemapStream. ${err}`, - ), - ); + strapi.log.error(logMessage(`Something went wrong while trying to build the SitemapStream. ${err}`)); throw new Error(); } }; From 7a5a7435b4ed8461459e1783939d47ff40ca8b18 Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Tue, 17 Oct 2023 21:27:26 -0400 Subject: [PATCH 14/19] revert controllers/settings.js --- .prettierrc.json | 4 ---- server/controllers/settings.js | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 .prettierrc.json diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index 12618fa..0000000 --- a/.prettierrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "trailingComma": "all", - "arrowParens": "always" -} diff --git a/server/controllers/settings.js b/server/controllers/settings.js index 7c9e2aa..f713447 100644 --- a/server/controllers/settings.js +++ b/server/controllers/settings.js @@ -17,9 +17,7 @@ module.exports = { updateSettings: async (ctx) => { const config = await getService('settings').getConfig(); - const newContentTypes = Object.keys(ctx.request.body.contentTypes).filter( - (x) => !Object.keys(config.contentTypes).includes(x), - ); + const newContentTypes = Object.keys(ctx.request.body.contentTypes).filter((x) => !Object.keys(config.contentTypes).includes(x)); await strapi .store({ From e9e78fb500c8f180b1488f5c723bb20dacd5faeb Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Tue, 17 Oct 2023 21:29:33 -0400 Subject: [PATCH 15/19] revert utils/index.js --- server/utils/index.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/server/utils/index.js b/server/utils/index.js index 41fb8a3..a75e77f 100644 --- a/server/utils/index.js +++ b/server/utils/index.js @@ -12,17 +12,14 @@ const logMessage = (msg = '') => `[strapi-plugin-sitemap]: ${msg}`; const noLimit = async (strapi, queryString, parameters, limit = 5000) => { let entries = []; - const amountOfEntries = await strapi.entityService.count( - queryString, - parameters, - ); + const amountOfEntries = await strapi.entityService.count(queryString, parameters); - for (let i = 0; i < amountOfEntries / limit; i++) { + for (let i = 0; i < (amountOfEntries / limit); i++) { /* eslint-disable-next-line */ const chunk = await strapi.entityService.findMany(queryString, { ...parameters, limit: limit, - start: i * limit, + start: (i * limit), }); if (chunk.id) { entries = [chunk, ...entries]; @@ -42,9 +39,7 @@ const formatCache = (cache, invalidationObject) => { Object.keys(invalidationObject).map((contentType) => { // Remove the items from the cache that will be refreshed. if (contentType && invalidationObject[contentType].ids) { - invalidationObject[contentType].ids.map( - (id) => delete cache[contentType]?.[id], - ); + invalidationObject[contentType].ids.map((id) => delete cache[contentType]?.[id]); } else if (contentType) { delete cache[contentType]; } @@ -52,7 +47,10 @@ const formatCache = (cache, invalidationObject) => { Object.values(cache).map((values) => { if (values) { - formattedCache = [...formattedCache, ...Object.values(values)]; + formattedCache = [ + ...formattedCache, + ...Object.values(values), + ]; } }); } From 75a60803baf70e636615adc9cd2211795713264f Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Tue, 17 Oct 2023 21:31:29 -0400 Subject: [PATCH 16/19] revert services/query.js --- server/services/query.js | 221 +++++++++++++++------------------------ 1 file changed, 84 insertions(+), 137 deletions(-) diff --git a/server/services/query.js b/server/services/query.js index 09a18ec..4196c89 100644 --- a/server/services/query.js +++ b/server/services/query.js @@ -24,23 +24,12 @@ const { noLimit, getService, logMessage } = require('../utils'); * * @returns {array} The fields. */ -const getFieldsFromConfig = ( - contentType, - topLevel = false, - isLocalized = false, - relation = null, -) => { +const getFieldsFromConfig = (contentType, topLevel = false, isLocalized = false, relation = null) => { let fields = []; if (contentType) { Object.entries(contentType['languages']).map(([langcode, { pattern }]) => { - fields.push( - ...getService('pattern').getFieldsFromPattern( - pattern, - topLevel, - relation, - ), - ); + fields.push(...getService('pattern').getFieldsFromPattern(pattern, topLevel, relation)); }); } @@ -93,18 +82,11 @@ const getRelationsFromConfig = (contentType) => { * @returns {object} The pages. */ const getPages = async (config, contentType, ids) => { - const excludeDrafts = - config.excludeDrafts && - strapi.contentTypes[contentType].options.draftAndPublish; - const isLocalized = - strapi.contentTypes[contentType].pluginOptions?.i18n?.localized; + const excludeDrafts = config.excludeDrafts && strapi.contentTypes[contentType].options.draftAndPublish; + const isLocalized = strapi.contentTypes[contentType].pluginOptions?.i18n?.localized; const relations = getRelationsFromConfig(config.contentTypes[contentType]); - const fields = getFieldsFromConfig( - config.contentTypes[contentType], - true, - isLocalized, - ); + const fields = getFieldsFromConfig(config.contentTypes[contentType], true, isLocalized); const pages = await noLimit(strapi, contentType, { filters: { @@ -120,7 +102,9 @@ const getPages = async (config, contentType, ids) => { }, }, ], - id: ids ? { $in: ids } : {}, + id: ids ? { + $in: ids, + } : {}, }, locale: 'all', fields, @@ -147,8 +131,7 @@ const getPages = async (config, contentType, ids) => { * @returns {object} The pages. */ const getLocalizationIds = async (contentType, ids) => { - const isLocalized = - strapi.contentTypes[contentType].pluginOptions?.i18n?.localized; + const isLocalized = strapi.contentTypes[contentType].pluginOptions?.i18n?.localized; const localizationIds = []; if (isLocalized) { @@ -174,12 +157,7 @@ const getLocalizationIds = async (contentType, ids) => { * * @returns {object} The invalidation object. */ -const composeInvalidationObject = async ( - config, - type, - queryFilters, - ids = [], -) => { +const composeInvalidationObject = async (config, type, queryFilters, ids = []) => { const mainIds = [...ids]; if (ids.length === 0) { @@ -195,50 +173,42 @@ const composeInvalidationObject = async ( // Add the updated entity. const invalidationObject = { [type]: { - ids: [...mainLocaleIds, ...mainIds], + ids: [ + ...mainLocaleIds, + ...mainIds, + ], }, }; // Add all pages that have a relation to the updated entity. - await Promise.all( - Object.keys(config.contentTypes).map(async (contentType) => { - const relations = Object.keys( - getRelationsFromConfig(config.contentTypes[contentType]), - ); - - await Promise.all( - relations.map(async (relation) => { - if ( - strapi.contentTypes[contentType].attributes[relation].target === - type - ) { - const pagesToUpdate = await strapi.entityService.findMany( - contentType, - { - filters: { [relation]: mainIds }, - fields: ['id'], - }, - ); - - if (pagesToUpdate.length > 0 && !invalidationObject[contentType]) { - invalidationObject[contentType] = {}; - } - - const relatedIds = []; - pagesToUpdate.map((page) => relatedIds.push(page.id)); - const relatedLocaleIds = await getLocalizationIds( - contentType, - relatedIds, - ); - - invalidationObject[contentType] = { - ids: [...relatedLocaleIds, ...relatedIds], - }; - } - }), - ); - }), - ); + await Promise.all(Object.keys(config.contentTypes).map(async (contentType) => { + const relations = Object.keys(getRelationsFromConfig(config.contentTypes[contentType])); + + await Promise.all(relations.map(async (relation) => { + if (strapi.contentTypes[contentType].attributes[relation].target === type) { + + const pagesToUpdate = await strapi.entityService.findMany(contentType, { + filters: { [relation]: mainIds }, + fields: ['id'], + }); + + if (pagesToUpdate.length > 0 && !invalidationObject[contentType]) { + invalidationObject[contentType] = {}; + } + + const relatedIds = []; + pagesToUpdate.map((page) => relatedIds.push(page.id)); + const relatedLocaleIds = await getLocalizationIds(contentType, relatedIds); + + invalidationObject[contentType] = { + ids: [ + ...relatedLocaleIds, + ...relatedIds, + ], + }; + } + })); + })); return invalidationObject; }; @@ -253,16 +223,13 @@ const composeInvalidationObject = async ( * @returns {void} */ const getSitemap = async (name, delta, fields = ['sitemap_string']) => { - const sitemap = await strapi.entityService.findMany( - 'plugin::sitemap.sitemap', - { - filters: { - name, - delta, - }, - fields, + const sitemap = await strapi.entityService.findMany('plugin::sitemap.sitemap', { + filters: { + name, + delta, }, - ); + fields, + }); return sitemap[0]; }; @@ -275,21 +242,16 @@ const getSitemap = async (name, delta, fields = ['sitemap_string']) => { * @returns {void} */ const deleteSitemap = async (name) => { - const sitemaps = await strapi.entityService.findMany( - 'plugin::sitemap.sitemap', - { - filters: { - name, - }, - fields: ['id'], + const sitemaps = await strapi.entityService.findMany('plugin::sitemap.sitemap', { + filters: { + name, }, - ); + fields: ['id'], + }); - await Promise.all( - sitemaps.map(async (sm) => { - await strapi.entityService.delete('plugin::sitemap.sitemap', sm.id); - }), - ); + await Promise.all(sitemaps.map(async (sm) => { + await strapi.entityService.delete('plugin::sitemap.sitemap', sm.id); + })); }; /** @@ -300,17 +262,18 @@ const deleteSitemap = async (name) => { * @returns {void} */ const createSitemap = async (data) => { - const { name, delta, type, sitemap_string } = data; + const { + name, + delta, + type, + sitemap_string, + } = data; let linkCount = null; parser.parseString(sitemap_string, (error, result) => { if (error) { - strapi.log.error( - logMessage( - `An error occurred while trying to parse the sitemap XML to json. ${error}`, - ), - ); + strapi.log.error(logMessage(`An error occurred while trying to parse the sitemap XML to json. ${error}`)); throw new Error(); } else if (type === 'index') { linkCount = get(result, 'sitemapindex.sitemap.length') || 0; @@ -342,21 +305,15 @@ const createSitemap = async (data) => { * @returns {void} */ const createSitemapCache = async (sitemapJson, name, sitemapId) => { - const sitemap = await strapi.entityService.findMany( - 'plugin::sitemap.sitemap-cache', - { - filters: { - name, - }, - fields: ['id'], + const sitemap = await strapi.entityService.findMany('plugin::sitemap.sitemap-cache', { + filters: { + name, }, - ); + fields: ['id'], + }); if (sitemap[0]) { - await strapi.entityService.delete( - 'plugin::sitemap.sitemap-cache', - sitemap[0].id, - ); + await strapi.entityService.delete('plugin::sitemap.sitemap-cache', sitemap[0].id); } await strapi.entityService.create('plugin::sitemap.sitemap-cache', { @@ -378,28 +335,21 @@ const createSitemapCache = async (sitemapJson, name, sitemapId) => { * @returns {void} */ const updateSitemapCache = async (sitemapJson, name, sitemapId) => { - const sitemap = await strapi.entityService.findMany( - 'plugin::sitemap.sitemap-cache', - { - filters: { - name, - }, - fields: ['id'], + const sitemap = await strapi.entityService.findMany('plugin::sitemap.sitemap-cache', { + filters: { + name, }, - ); + fields: ['id'], + }); if (sitemap[0]) { - await strapi.entityService.update( - 'plugin::sitemap.sitemap-cache', - sitemap[0].id, - { - data: { - sitemap_json: sitemapJson, - sitemap_id: sitemapId, - name, - }, + await strapi.entityService.update('plugin::sitemap.sitemap-cache', sitemap[0].id, { + data: { + sitemap_json: sitemapJson, + sitemap_id: sitemapId, + name, }, - ); + }); } }; @@ -411,14 +361,11 @@ const updateSitemapCache = async (sitemapJson, name, sitemapId) => { * @returns {void} */ const getSitemapCache = async (name) => { - const sitemap = await strapi.entityService.findMany( - 'plugin::sitemap.sitemap-cache', - { - filters: { - name, - }, + const sitemap = await strapi.entityService.findMany('plugin::sitemap.sitemap-cache', { + filters: { + name, }, - ); + }); return sitemap[0]; }; From e77d855f751dc5e934f5ac6584f296415a3d1d97 Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Tue, 17 Oct 2023 21:34:35 -0400 Subject: [PATCH 17/19] clean up --- server/services/pattern.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/server/services/pattern.js b/server/services/pattern.js index 1126ec0..ef86f6e 100644 --- a/server/services/pattern.js +++ b/server/services/pattern.js @@ -14,16 +14,10 @@ */ const getAllowedFields = (contentType, allowedFields = []) => { const fields = []; - const fieldTypes = - allowedFields.length > 0 - ? allowedFields - : strapi.config.get('plugin.sitemap.allowedFields'); + const fieldTypes = allowedFields.length > 0 ? allowedFields : strapi.config.get('plugin.sitemap.allowedFields'); fieldTypes.map((fieldType) => { Object.entries(contentType.attributes).map(([fieldName, field]) => { - if ( - (field.type === fieldType || fieldName === fieldType) && - field.type !== 'relation' - ) { + if ((field.type === fieldType || fieldName === fieldType) && field.type !== 'relation') { fields.push(fieldName); } else if ( field.type === 'relation' && @@ -67,7 +61,10 @@ const getAllowedFields = (contentType, allowedFields = []) => { ) { const relation = strapi.components[field.component]; - if (fieldTypes.includes('id') && !fields.includes(`${fieldName}.id`)) { + if ( + fieldTypes.includes('id') + && !fields.includes(`${fieldName}.id`) + ) { fields.push(`${fieldName}.id`); } From 7188cca10323159b34675d44812cb361b4b051a8 Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Wed, 17 Jul 2024 21:55:01 -0400 Subject: [PATCH 18/19] fixed manyToMany with inversedBy --- server/services/pattern.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/services/pattern.js b/server/services/pattern.js index ef86f6e..a36c661 100644 --- a/server/services/pattern.js +++ b/server/services/pattern.js @@ -41,7 +41,7 @@ const getAllowedFields = (contentType, allowedFields = []) => { } else if ( field.type === 'relation' && field.target && - field.mappedBy && + (field.mappedBy || field.inversedBy) && field.relation.endsWith('ToMany') && fieldName !== 'localizations' && fieldName !== 'createdBy' && @@ -122,6 +122,8 @@ const getFieldsFromPattern = (pattern, topLevel = false, relation = null) => { * @returns {array} The relations. */ const getRelationsFromPattern = (pattern) => { + console.log('getRelationsFromPattern pattern', pattern); + let fields = getFieldsFromPattern(pattern); fields = fields.filter((field) => field.split('.').length > 1); // Filter on fields containing a dot (.) From d2cebf1cc2407c364421f33993bf07f06070c2a3 Mon Sep 17 00:00:00 2001 From: Candido Sales Gomes Date: Wed, 17 Jul 2024 21:55:41 -0400 Subject: [PATCH 19/19] removed log --- server/services/pattern.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/services/pattern.js b/server/services/pattern.js index a36c661..58533da 100644 --- a/server/services/pattern.js +++ b/server/services/pattern.js @@ -122,8 +122,6 @@ const getFieldsFromPattern = (pattern, topLevel = false, relation = null) => { * @returns {array} The relations. */ const getRelationsFromPattern = (pattern) => { - console.log('getRelationsFromPattern pattern', pattern); - let fields = getFieldsFromPattern(pattern); fields = fields.filter((field) => field.split('.').length > 1); // Filter on fields containing a dot (.)