diff --git a/apps/scandic-redirect/.gitignore b/apps/scandic-redirect/.gitignore new file mode 100644 index 000000000..2b0539e64 --- /dev/null +++ b/apps/scandic-redirect/.gitignore @@ -0,0 +1,2 @@ +scripts/csv/*.csv +scripts/json/*.json diff --git a/apps/scandic-redirect/README.md b/apps/scandic-redirect/README.md index 6451f8648..fa850875c 100644 --- a/apps/scandic-redirect/README.md +++ b/apps/scandic-redirect/README.md @@ -6,3 +6,20 @@ This function will be called by the `web` app's middleware to check if the incom The "source of truth" for which URLs should be redirected where will be provided by the SEO team and put in a JSON file within this app. If no match for the incoming request is found, the request is passed on through the middleware. + +## Update the redirects from the source + +The Excel source file used is located at: + +https://scandichotelsab.sharepoint.com/:x:/s/921-ContentNewweb/ETGStOQAARtJhJXG9dy8ijYBccpmKhLVjS2SF_2E69QrAQ + +- Open it +- Each domain/language has its own sheet +- Export each sheet into their respective language code + - File > Export > Download as CSV UTF-8 + - Save as [lang].csv in `./scripts/csv` folder +- Run the `update` script target + - E.g. `yarn workspace @scandic-hotels/scandic-redirect update` +- Commit and push the JSON files in `./netlify/functions/data`. +- Create a PR +- Profit! diff --git a/apps/scandic-redirect/package.json b/apps/scandic-redirect/package.json index 5d0cc9ee5..27e775a85 100644 --- a/apps/scandic-redirect/package.json +++ b/apps/scandic-redirect/package.json @@ -5,5 +5,11 @@ "packageManager": "yarn@4.6.0", "dependencies": { "@netlify/functions": "^3.0.0" + }, + "devDependencies": { + "convert-csv-to-json": "^3.4.0" + }, + "scripts": { + "update": "node ./scripts/update.mjs" } } diff --git a/apps/scandic-redirect/scripts/csv/.gitkeep b/apps/scandic-redirect/scripts/csv/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/scandic-redirect/scripts/json/.gitkeep b/apps/scandic-redirect/scripts/json/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/scandic-redirect/scripts/update.mjs b/apps/scandic-redirect/scripts/update.mjs new file mode 100644 index 000000000..fd05ee9f4 --- /dev/null +++ b/apps/scandic-redirect/scripts/update.mjs @@ -0,0 +1,138 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +import csvToJson from 'convert-csv-to-json'; + +const langs = ['da', 'de', 'en', 'fi', 'no', 'sv']; +const csvHeaders = { + current: 'Current URL', + redirect: 'Redirect URL', +}; + +function csvFilePath(lang) { + return `${import.meta.dirname}/csv/${lang}.csv`; +} + +function jsonFilePath(lang) { + return `${import.meta.dirname}/json/${lang}.json`; +} + +function outputFilepath(lang) { + return path.resolve( + import.meta.dirname, + `../netlify/functions/data/${lang}.json` + ); +} + +function removeDomain(str) { + return str.replace( + /^https?:\/\/((www|test|stage|prod)\.)?scandichotels.(com|de|dk|fi|no|se)/, + '' + ); +} + +function akamaiRedirect(str) { + return str.replace( + /^https?:\/\/((www|test|stage|prod)\.)?scandichotels.(com|de|dk|fi|no|se)/, + (...match) => { + if (match[3]) { + switch (match[3]) { + case 'com': + return '/en'; + case 'de': + return '/de'; + case 'dk': + return '/da'; + case 'fi': + return '/fi'; + case 'no': + return '/no'; + case 'se': + return '/sv'; + } + } + return ''; + } + ); +} + +function checkPrerequisites() { + const missingLangs = langs.reduce((acc, lang) => { + const filepath = csvFilePath(lang); + if (!fs.existsSync(filepath)) { + acc.push(filepath); + } + return acc; + }, []); + + if (missingLangs.length > 0) { + console.error(`Missing CSV file:\n${missingLangs.join('\n')}`); + process.exit(1); + } +} + +// convert-csv-to-json writes async without callback support +// so we workaround it be overriding console.log which it uses when it is done +async function convertCsvToJson() { + return new Promise((resolve, reject) => { + const _consoleLog = console.log; + let resolved = 0; + console.log = function (str) { + if (str.indexOf('File saved:') >= 0) { + resolved++; + } + + if (resolved === langs.length) { + console.log = _consoleLog; + resolve(); + } + }; + + for (const lang of langs) { + csvToJson + .utf8Encoding() + .fieldDelimiter(',') + .generateJsonFileFromCsv(csvFilePath(lang), jsonFilePath(lang)); + } + + setTimeout(() => { + reject('timeout'); + }, 5000); + }); +} + +async function makeOutput() { + for (const lang of langs) { + try { + const json = JSON.parse( + fs.readFileSync(jsonFilePath(lang), { + encoding: 'utf-8', + }) + ); + + if (Array.isArray(json)) { + const finalUrls = json.reduce((acc, url) => { + const from = removeDomain(akamaiRedirect(url[csvHeaders.current])); + const to = removeDomain(url[csvHeaders.redirect]); + + return { + ...acc, + [from]: to, + }; + }, {}); + + fs.writeFileSync(outputFilepath(lang), JSON.stringify(finalUrls), { + encoding: 'utf-8', + }); + } else { + throw new Error(`JSON was not an array: ${jsonFilePath(lang)}`); + } + } catch (e) { + console.error(e); + } + } +} + +checkPrerequisites(); +await convertCsvToJson(); +await makeOutput(); diff --git a/yarn.lock b/yarn.lock index 9f1b2d946..bb8c1dc84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7052,6 +7052,7 @@ __metadata: resolution: "@scandic-hotels/scandic-redirect@workspace:apps/scandic-redirect" dependencies: "@netlify/functions": "npm:^3.0.0" + convert-csv-to-json: "npm:^3.4.0" languageName: unknown linkType: soft @@ -11775,6 +11776,13 @@ __metadata: languageName: node linkType: hard +"convert-csv-to-json@npm:^3.4.0": + version: 3.4.0 + resolution: "convert-csv-to-json@npm:3.4.0" + checksum: 10c0/06ec3cb348591322b9b2083464eff41907688d6eb4cb8b364d7d969ea08eb559d872733292034f72dcf404fe3150def092194ccfe14d89ded3b35dfc694fdb87 + languageName: node + linkType: hard + "convert-source-map@npm:^2.0.0": version: 2.0.0 resolution: "convert-source-map@npm:2.0.0"