From 1fa8007e0e037fd15fcc74679b0494e345d00906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Thu, 20 Jun 2019 15:08:22 +0200 Subject: [PATCH] RSS function --- package-lock.json | 14 +++--- package.json | 2 +- src/config.default.json | 3 ++ src/rss.js | 40 ++++++++++++++++ test/rss.test.js | 100 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 152 insertions(+), 7 deletions(-) create mode 100644 src/rss.js create mode 100644 test/rss.test.js diff --git a/package-lock.json b/package-lock.json index 7967736..56e9b3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8670,8 +8670,7 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "semver": { "version": "5.7.0", @@ -9670,10 +9669,13 @@ "async-limiter": "~1.0.0" } }, - "xml": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", - "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=" + "xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "requires": { + "sax": "^1.2.4" + } }, "xml-name-validator": { "version": "3.0.0", diff --git a/package.json b/package.json index 7ca4d39..5136935 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "express": "^4.17.1", "ncp": "^2.0.0", "showdown": "^1.9.0", - "xml": "^1.0.1" + "xml-js": "^1.6.11" }, "devDependencies": { "babel-cli": "^6.26.0", diff --git a/src/config.default.json b/src/config.default.json index 25acebc..88c5156 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -2,6 +2,7 @@ "node_port": 3000, "data_dir": "data", "view_engine": "ejs", + "loopback_url": "http://localhost:3000", "modules": { "plantuml": false, "rss": true, @@ -22,6 +23,8 @@ "default_thumbnail": null }, "rss": { + "title": "mygitblog RSS feed", + "description": "a generated RSS feed from my articles", "endpoint": "/rss", "length": 10 }, diff --git a/src/rss.js b/src/rss.js new file mode 100644 index 0000000..efd36aa --- /dev/null +++ b/src/rss.js @@ -0,0 +1,40 @@ +const convert = require('xml-js'); + +const mapArticle = (url, article) => { + return { + 'title': {'_text': article.title}, + 'link': {'_text': (url + article.url).replace(/([^:])\/\//g, '$1/')}, + 'pubDate': {'_text': article.date.toString()}, + }; +}; + +module.exports = (config) => { + return { + get: (dict) => { + const items = Object.values(dict) + .sort((a, b) => ('' + b.path).localeCompare(a.path)) + .slice(0, config['rss']['length']); + const data = { + '_declaration': { + '_attributes': { + 'version': '1.0', + 'encoding': 'UTF-8' + } + }, + 'rss': { + '_attributes': { + 'version': '2.0' + }, + 'title': {'_text': config['rss']['title']}, + 'description': {'_text': config['rss']['description']}, + 'link': {'_text': config['loopback_url']}, + 'lastBuildDate': {'_text': new Date().toString()}, + 'lastPubDate': {'_text': new Date().toString()}, + 'ttl': {'_text': '60'}, + 'item': items.map((a) => mapArticle(config['loopback_url'], a)) + } + }; + return convert.js2xml(data, {compact: true}); + } + }; +}; \ No newline at end of file diff --git a/test/rss.test.js b/test/rss.test.js new file mode 100644 index 0000000..087a2eb --- /dev/null +++ b/test/rss.test.js @@ -0,0 +1,100 @@ +/* jshint -W117 */ +const config = { + 'loopback_url': 'http://test.test/', + 'rss': { + 'title': 'test rss', + 'description': 'description', + 'endpoint': '/rss', + 'length': 2 + }, +}; + +const rss = require('../src/rss')(config); + +test('empty rss', () => { + const xml = rss.get({}); + expect(xml).toBe('' + + '' + + 'test rss' + + 'description' + + 'http://test.test/' + + '' + new Date().toString() + '' + + '' + new Date().toString() + '' + + '60' + + ''); +}); + +test('1 item', () => { + const data = { + 'a': { + path: 'a', + realPath: 'b', + year: 2019, + month: 5, + day: 5, + date: new Date(2019, 5, 5), + title: 'Title with : info !', + thumbnail: '/2019/05/05/thumbnail.jpg/', + escapedTitle: 'title_with___info', + url: '/2019/05/05/title_with___info/', + } + }; + const xml = rss.get(data); + expect(xml).toEqual('' + + '' + + 'test rss' + + 'description' + + 'http://test.test/' + + '' + new Date().toString() + '' + + '' + new Date().toString() + '' + + '60' + + '' + + 'Title with : info !' + + 'http://test.test/2019/05/05/title_with___info/' + + '' + new Date(2019, 5, 5).toString() + '' + + '' + + ''); +}); + +test('3 items only 2 shown sorted', () => { + const data = { + 'a': { + path: '2019/05/05', + date: new Date(2019, 5, 5), + title: 'a', + url: 'a', + }, + 'b': { + path: '2018/05/05', + date: new Date(2018, 5, 5), + title: 'b', + url: 'b', + }, + 'c': { + path: '2020/05/05', + date: new Date(2020, 5, 5), + title: 'c', + url: 'c', + } + }; + const xml = rss.get(data); + expect(xml).toEqual('' + + '' + + 'test rss' + + 'description' + + 'http://test.test/' + + '' + new Date().toString() + '' + + '' + new Date().toString() + '' + + '60' + + '' + + 'c' + + 'http://test.test/c' + + '' + new Date(2020, 5, 5).toString() + '' + + '' + + '' + + 'a' + + 'http://test.test/a' + + '' + new Date(2019, 5, 5).toString() + '' + + '' + + ''); +}); \ No newline at end of file