From 0d423500cf0fc0e56d8c6fdb396a535bc691d3c9 Mon Sep 17 00:00:00 2001 From: Thomas Boutell Date: Fri, 24 Sep 2021 13:22:05 -0400 Subject: [PATCH] rewriteUrl method --- CHANGELOG.md | 4 + README.md | 15 ++++ index.js | 12 ++- package.json | 4 +- test/package.json | 7 ++ test/test-rewrite-url.js | 177 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 215 insertions(+), 4 deletions(-) create mode 100644 test/package.json create mode 100644 test/test-rewrite-url.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bb2f29ce..82a9c016f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## UNRELEASED + +Introduced a `rewriteUrl` method, which project developers can override to customize the URLs being output in the sitemap. + ## 2.5.3 Disables the `siteMapPriority` schema field on the `trash` page to prevent an "unarranged field" warning about it. diff --git a/README.md b/README.md index d73535290..2e936dd8f 100644 --- a/README.md +++ b/README.md @@ -242,3 +242,18 @@ modules: { } } ``` + +## Rewriting URLs + +Normally the URLs output by this module are just what you'll want. However if Apostrophe is acting as a headless backend the URLs generated in the sitemap will point to that backend site and not necessarily to the right public URL. To customize the URLs, override the `rewriteUrl` method at project level, like this: + +```javascript +// in your lib/modules/apostrophe-site-map/index.js file at project level +// (do not alter it in node_modules) +module.exports = { + construct(self, options) { + self.rewriteUrl = url => { + return url.replace('https://onesite.com', 'https://anothersite.com'); + }; + } +}; diff --git a/index.js b/index.js index 29124132d..7b2b247f8 100644 --- a/index.js +++ b/index.js @@ -477,10 +477,10 @@ module.exports = { self.write(locale, ' '); } - self.write(locale, page._url + '\n'); + self.write(locale, self.rewriteUrl(page._url) + '\n'); } } else { - url = page._url; + url = self.rewriteUrl(page._url); var priority = (page.level < 10) ? (1.0 - page.level / 10) : 0.1; if (typeof (page.siteMapPriority) === 'number') { @@ -573,5 +573,13 @@ module.exports = { self.enableCache = function() { self.cache = self.apos.caches.get('apostrophe-sitemap'); }; + + // Override this method at project level to customize the URLs output in the sitemap. + // Useful in headless applications where the URLs visible to Apostrophe, + // acting as a backend, differ from those visible to the public + + self.rewriteUrl = url => { + return url; + } } }; diff --git a/package.json b/package.json index 3ee26ddc9..43e0cb7b6 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "mocha": "^5.1.1" }, "scripts": { - "test": "mocha test/test-workflow.js && mocha test/test-simple.js" + "test": "mocha" }, "repository": { "type": "git", @@ -33,4 +33,4 @@ "url": "https://github.com/apostrophecms/apostrophe-site-map/issues" }, "homepage": "https://github.com/apostrophecms/apostrophe-site-map" -} \ No newline at end of file +} diff --git a/test/package.json b/test/package.json new file mode 100644 index 000000000..a8094fe67 --- /dev/null +++ b/test/package.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "apostrophe": "^2.0.0", + "apostrophe-site-map": "^2.0.0", + "apostrophe-workflow": "^2.0.0" + } +} diff --git a/test/test-rewrite-url.js b/test/test-rewrite-url.js new file mode 100644 index 000000000..b3ed46b5b --- /dev/null +++ b/test/test-rewrite-url.js @@ -0,0 +1,177 @@ +var assert = require('assert'); +var _ = require('@sailshq/lodash'); +var async = require('async'); +var request = require('request'); +var fs = require('fs'); + +describe('Apostrophe Sitemap: test rewrite URL', function() { + + var apos; + + this.timeout(5000); + + after(function(done) { + try { + require('apostrophe/test-lib/util').destroy(apos, done); + } catch (e) { + console.warn('Old version of apostrophe does not export test-lib/util library, just dropping old test db'); + apos.db.dropDatabase(); + setTimeout(done, 1000); + } + }); + + it('should be a property of the apos object', function(done) { + apos = require('apostrophe')({ + testModule: true, + baseUrl: 'http://localhost:7780', + modules: { + 'apostrophe-express': { + port: 7780 + }, + 'apostrophe-site-map': { + construct(self, options) { + self.rewriteUrl = url => { + return url.replace('http://localhost:7780', 'https://public-site.com'); + } + } + }, + 'apostrophe-pages': { + park: [ + { + title: 'Tab One', + type: 'default', + slug: '/tab-one', + _children: [ + { + title: 'Tab One Child One', + type: 'default', + slug: '/tab-one/child-one' + }, + { + title: 'Tab One Child Two', + type: 'default', + slug: '/tab-one/child-two' + }, + ] + }, + { + title: 'Tab Two', + type: 'default', + slug: '/tab-two', + _children: [ + { + title: 'Tab Two Child One', + type: 'default', + slug: '/tab-two/child-one' + }, + { + title: 'Tab Two Child Two', + type: 'default', + slug: '/tab-two/child-two' + }, + ] + }, + { + title: 'Products', + type: 'products-page', + slug: '/products' + } + ], + types: [ + { + name: 'home', + label: 'Home' + }, + { + name: 'default', + label: 'Default' + }, + { + name: 'products', + label: 'Products' + } + ] + }, + 'products': { + extend: 'apostrophe-pieces', + name: 'product' + }, + 'products-pages': { + extend: 'apostrophe-pieces-pages' + }, + }, + afterInit: function(callback) { + assert(apos.modules['apostrophe-site-map']); + return callback(null); + }, + afterListen: function(err) { + done(); + } + }); + }); + + it('insert a product for test purposes', function(done) { + var product = _.assign(apos.modules.products.newInstance(), { + title: 'Cheese', + slug: 'cheese' + }); + apos.modules.products.insert(apos.tasks.getReq(), product, function(err) { + assert(!err); + done(); + }); + }); + + it('make sure everything is published and out of the trash for test purposes', function(done) { + return apos.docs.db.update({}, { + $set: { + trash: false, + published: true + } + }, { + multi: true + }, function(err, count) { + assert(!err); + done(); + }); + }); + + it('insert an unpublished product for test purposes', function(done) { + var product = _.assign(apos.modules.products.newInstance(), { + title: 'Rocks', + slug: 'rocks', + published: false + }); + apos.modules.products.insert(apos.tasks.getReq(), product, function(err) { + assert(!err); + done(); + }); + }); + + it('should generate a suitable sitemap', function(done) { + this.timeout(10000); + get('http://localhost:7780/sitemap.xml', function(err, xml) { + if (err) { + console.error(err); + } + assert(!err); + assert(xml); + assert(xml.indexOf('https://public-site.com/') !== -1); + assert(xml.indexOf('https://public-site.com/tab-one') !== -1); + assert(xml.indexOf('https://public-site.com/tab-two') !== -1); + assert(xml.indexOf('https://public-site.com/tab-one/child-one') !== -1); + assert(xml.indexOf('https://public-site.com/products/cheese') !== -1); + assert(xml.indexOf('https://public-site.com/products/rocks') === -1); + done(); + }); + }); + +}); + +function get(url, callback) { + return request(url, function(err, response, body) { + if (err || (response.statusCode >= 400)) { + return callback(err || response.statusCode); + } + return callback(null, body); + }); +}