From a31f95eefe16cbf87c10c761578b6489155dace2 Mon Sep 17 00:00:00 2001 From: frozenhelium Date: Tue, 30 Apr 2024 14:16:16 +0545 Subject: [PATCH] WIP --- .../commands/applyMigrations.test.ts | 10 +- .../translatte/commands/applyMigrations.ts | 41 +++--- .../translatte/commands/exportStrings.ts | 23 ++++ .../translatte/commands/generateMigration.ts | 4 +- .../translatte/commands/importExcel.ts | 60 +++++---- .../translatte/commands/mergeMigrations.ts | 4 +- .../translatte/commands/pushMigration.ts | 52 +++++++- app/scripts/translatte/commands/uploadJson.ts | 82 ++++++++++++ app/scripts/translatte/main.ts | 92 +++++++++++-- app/scripts/translatte/types.ts | 13 +- app/scripts/translatte/utils.ts | 124 ++++++++++++++++-- .../000003-1712631208970.json | 2 +- 12 files changed, 419 insertions(+), 88 deletions(-) create mode 100644 app/scripts/translatte/commands/exportStrings.ts create mode 100644 app/scripts/translatte/commands/uploadJson.ts diff --git a/app/scripts/translatte/commands/applyMigrations.test.ts b/app/scripts/translatte/commands/applyMigrations.test.ts index 735dbea4cd..4d01409e4f 100644 --- a/app/scripts/translatte/commands/applyMigrations.test.ts +++ b/app/scripts/translatte/commands/applyMigrations.test.ts @@ -4,7 +4,7 @@ import { join } from 'path'; import { testWithTmpDir } from '../testHelpers'; import { - writeFilePromisify, + writeFileAsync, readJsonFilesContents, } from '../utils'; import { @@ -29,7 +29,7 @@ testWithTmpDir('test applyMigrations with no data in server', async ({ tmpdir }) { name: '000003-1000000000000.json', content: migrationContent3 }, { name: '000004-1000000000000.json', content: migrationContent4 }, { name: '000005-1000000000000.json', content: migrationContent5 }, - ].map(({ name, content }) => writeFilePromisify( + ].map(({ name, content }) => writeFileAsync( join(tmpdir, 'migrations', name), JSON.stringify(content, null, 4), 'utf8', @@ -42,7 +42,7 @@ testWithTmpDir('test applyMigrations with no data in server', async ({ tmpdir }) last_migration: undefined, strings: [], }; - await writeFilePromisify( + await writeFileAsync( join(tmpdir, 'strings', 'before.json'), JSON.stringify(emptySourceFile), 'utf8', @@ -70,7 +70,7 @@ testWithTmpDir('test applyMigrations with data in server', async ({ tmpdir }) => mkdirSync(join(tmpdir, 'migrations')); const migrations = [ { name: '000006-1000000000000.json', content: migrationContent6 }, - ].map(({ name, content }) => writeFilePromisify( + ].map(({ name, content }) => writeFileAsync( join(tmpdir, 'migrations', name), JSON.stringify(content, null, 4), 'utf8', @@ -79,7 +79,7 @@ testWithTmpDir('test applyMigrations with data in server', async ({ tmpdir }) => mkdirSync(join(tmpdir, 'strings')); - await writeFilePromisify( + await writeFileAsync( join(tmpdir, 'strings', 'before.json'), JSON.stringify(strings1), 'utf8', diff --git a/app/scripts/translatte/commands/applyMigrations.ts b/app/scripts/translatte/commands/applyMigrations.ts index 67aaaa0594..03ea74de80 100644 --- a/app/scripts/translatte/commands/applyMigrations.ts +++ b/app/scripts/translatte/commands/applyMigrations.ts @@ -1,11 +1,11 @@ import { Md5 } from 'ts-md5'; import { listToMap, isDefined, unique } from '@togglecorp/fujs'; -import { isAbsolute, join, basename } from 'path'; +import { basename } from 'path'; import { - readSource, - getMigrationFilesAttrs, + readJsonSource, readMigrations, - writeFilePromisify, + writeFileAsync, + getMigrationFilesAttrsFromDir, } from '../utils'; import { merge } from './mergeMigrations'; import { @@ -39,10 +39,13 @@ function apply( const prevValue = stringsMapping[key]; // NOTE: we are comparing hash instead of value so that this works for source language as well as other languages if (prevValue && prevValue.hash !== hash) { - throw `Add: We already have string with different value for namespace '${action.namespace}' and key '${action.key}'`; + // console.info(prevValue, action); + // throw `Add: We already have string with different value for namespace '${action.namespace}' and key '${action.key}'`; + console.info(`Add: We already have string with different value for namespace '${action.namespace}' and key '${action.key}'`); } if (newMapping[key]) { + console.info(prevValue, action, newMapping); throw `Add: We already have string for namespace '${action.namespace}' and key '${action.key}' in migration`; } @@ -114,20 +117,16 @@ function apply( } async function applyMigrations( - projectPath: string, - sourceFileName: string, + migrationDir: string, + sourceFilePath: string, destinationFileName: string, - migrationFilePath: string, languages: string[], from: string | undefined, dryRun: boolean | undefined, ) { - const sourcePath = isAbsolute(sourceFileName) - ? sourceFileName - : join(projectPath, sourceFileName) - const sourceFile = await readSource(sourcePath) + const sourceFile = await readJsonSource(sourceFilePath) - const migrationFilesAttrs = await getMigrationFilesAttrs(projectPath, migrationFilePath); + const migrationFilesAttrs = await getMigrationFilesAttrsFromDir(migrationDir); const selectedMigrationFilesAttrs = from ? migrationFilesAttrs.filter((item) => (item.migrationName > from)) : migrationFilesAttrs; @@ -149,25 +148,23 @@ async function applyMigrations( ); const outputSourceFileContent: SourceFileContent = { - ...sourceFile.content, + // ...sourceFile.content, last_migration: basename(lastMigration.file), strings: apply( - sourceFile.content.strings, + sourceFile.content.filter( + ({ page_name }) => isDefined(page_name) + ), mergedMigrationActions, languages, ), }; - const destinationPath = isAbsolute(destinationFileName) - ? destinationFileName - : join(projectPath, destinationFileName) - if (dryRun) { - console.info(`Creating file '${destinationPath}'`); + console.info(`Creating file '${destinationFileName}'`); console.info(outputSourceFileContent); } else { - await writeFilePromisify( - destinationPath, + await writeFileAsync( + destinationFileName, JSON.stringify(outputSourceFileContent, null, 4), 'utf8', ); diff --git a/app/scripts/translatte/commands/exportStrings.ts b/app/scripts/translatte/commands/exportStrings.ts new file mode 100644 index 0000000000..4852a2f5aa --- /dev/null +++ b/app/scripts/translatte/commands/exportStrings.ts @@ -0,0 +1,23 @@ +import { join } from 'path'; + +import { fetchAllServerStrings, writeFileAsync } from "../utils"; + +async function exportStrings( + apiUrl: string, + outputDir: string, +) { + const serverStrings = await fetchAllServerStrings(apiUrl); + + const url = new URL(apiUrl); + const now = new Date(); + const exportFileName = `${url.hostname}-${now.getTime()}.json`; + const exportFilePath = join(outputDir, exportFileName); + + await writeFileAsync( + exportFilePath, + JSON.stringify(serverStrings, null, 2), + 'utf8', + ); +} + +export default exportStrings; diff --git a/app/scripts/translatte/commands/generateMigration.ts b/app/scripts/translatte/commands/generateMigration.ts index b2a99f68b0..18152d06e7 100644 --- a/app/scripts/translatte/commands/generateMigration.ts +++ b/app/scripts/translatte/commands/generateMigration.ts @@ -2,7 +2,7 @@ import { Md5 } from 'ts-md5'; import { join, isAbsolute } from 'path'; import { - writeFilePromisify, + writeFileAsync, oneOneMapping, readTranslations, getTranslationFileNames, @@ -184,7 +184,7 @@ async function generate( console.info(`Creating migration file '${outputMigrationFile}'`); console.info(migrationContent); } else { - await writeFilePromisify( + await writeFileAsync( outputMigrationFile, JSON.stringify(migrationContent, null, 4), 'utf8', diff --git a/app/scripts/translatte/commands/importExcel.ts b/app/scripts/translatte/commands/importExcel.ts index 3016af0ca8..42d128d4e5 100644 --- a/app/scripts/translatte/commands/importExcel.ts +++ b/app/scripts/translatte/commands/importExcel.ts @@ -1,12 +1,16 @@ -import { isDefined, isNotDefined, listToGroupList, listToMap, mapToList } from '@togglecorp/fujs'; +import { compareString, isDefined, isNotDefined, isTruthyString, listToGroupList, listToMap, mapToList } from '@togglecorp/fujs'; import xlsx from 'exceljs'; -import { Language, ServerActionItem } from '../types'; +import { Language, ServerActionItem, SourceStringItem, StringItem } from '../types'; import { Md5 } from 'ts-md5'; -import { postLanguageStrings } from '../utils'; - -async function importExcel(importFilePath: string, apiUrl: string, accessToken: string) { +import { fetchAllServerStrings, postLanguageStrings, readFileAsync, writeFileAsync } from '../utils'; +import stagingStrings from '../../../../../../go-temp/goadmin-stage.ifrc.org-1717130893109.json'; + +async function importExcel( + importFilePath: string, + apiUrl: string, + accessToken: string, +) { const workbook = new xlsx.Workbook(); - await workbook.xlsx.readFile(importFilePath); const firstSheet = workbook.worksheets[0]; @@ -26,13 +30,7 @@ async function importExcel(importFilePath: string, apiUrl: string, accessToken: ({ column }) => column, ); - const strings: { - key: string; - namespace: string; - language: Language; - value: string; - hash: string; - }[] = []; + const stringsFromExcel: StringItem[] = []; firstSheet.eachRow( (row) => { @@ -64,28 +62,28 @@ async function importExcel(importFilePath: string, apiUrl: string, accessToken: const hash = Md5.hashStr(en); - strings.push({ - key, + stringsFromExcel.push({ namespace, + key, language: 'en', value: en, hash, }); if (isDefined(ar)) { - strings.push({ - key, + stringsFromExcel.push({ namespace, + key, language: 'ar', value: ar, - hash, + hash, }); } if (isDefined(fr)) { - strings.push({ - key, + stringsFromExcel.push({ namespace, + key, language: 'fr', value: fr, hash, @@ -93,9 +91,9 @@ async function importExcel(importFilePath: string, apiUrl: string, accessToken: } if (isDefined(es)) { - strings.push({ - key, + stringsFromExcel.push({ namespace, + key, language: 'es', value: es, hash, @@ -106,7 +104,7 @@ async function importExcel(importFilePath: string, apiUrl: string, accessToken: const languageGroupedActions = mapToList( listToGroupList( - strings, + stringsFromExcel, ({ language }) => language, (languageString) => { const serverAction: ServerActionItem = { @@ -126,6 +124,7 @@ async function importExcel(importFilePath: string, apiUrl: string, accessToken: }) ); + /* const postPromises = languageGroupedActions.map( (languageStrings) => postLanguageStrings( languageStrings.language, @@ -135,7 +134,20 @@ async function importExcel(importFilePath: string, apiUrl: string, accessToken: ) ) - await Promise.all(postPromises); + const postResponses = await Promise.all(postPromises); + + const postJsonResponses = await Promise.all( + postResponses.map((response) => response.json()) + ); + */ + + /* + await writeFileAsync( + 'serverResponse.json', + JSON.stringify(postJsonResponses, null, 2), + 'utf8', + ); + */ } export default importExcel; diff --git a/app/scripts/translatte/commands/mergeMigrations.ts b/app/scripts/translatte/commands/mergeMigrations.ts index 4be6b0f0ab..1b88787efa 100644 --- a/app/scripts/translatte/commands/mergeMigrations.ts +++ b/app/scripts/translatte/commands/mergeMigrations.ts @@ -7,7 +7,7 @@ import { getMigrationFilesAttrs, readMigrations, removeFiles, - writeFilePromisify + writeFileAsync } from '../utils'; function getCanonicalKey( @@ -209,7 +209,7 @@ async function mergeMigrations( console.info(`Creating migration file '${newFileName}'`); console.info(mergedMigrationContent); } else { - await writeFilePromisify( + await writeFileAsync( newFileName, JSON.stringify(mergedMigrationContent, null, 4), 'utf8', diff --git a/app/scripts/translatte/commands/pushMigration.ts b/app/scripts/translatte/commands/pushMigration.ts index bf7b1690fb..327def418d 100644 --- a/app/scripts/translatte/commands/pushMigration.ts +++ b/app/scripts/translatte/commands/pushMigration.ts @@ -1,6 +1,6 @@ -import { isDefined, isNotDefined, listToGroupList, listToMap, mapToMap } from "@togglecorp/fujs"; +import { isDefined, isNotDefined, isTruthyString, listToGroupList, listToMap, mapToMap } from "@togglecorp/fujs"; import { Language, MigrationActionItem, SourceStringItem } from "../types"; -import { fetchLanguageStrings, getCombinedKey, postLanguageStrings, readMigrations } from "../utils"; +import { fetchLanguageStrings, getCombinedKey, postLanguageStrings, readMigrations, writeFileAsync } from "../utils"; import { Md5 } from "ts-md5"; const languages: Language[] = ['en', 'fr', 'es', 'ar']; @@ -38,10 +38,13 @@ async function fetchServerState(apiUrl: string, authToken: string) { async function pushMigration(migrationFilePath: string, apiUrl: string, authToken: string) { const serverStrings = await fetchServerState(apiUrl, authToken); + // const serverStrings = prevServerStateStrings as SourceStringItem[]; const serverStringMapByCombinedKey = mapToMap( listToGroupList( - serverStrings, + serverStrings.filter( + ({ value, page_name }) => isTruthyString(page_name) && isTruthyString(value), + ), ({ key, page_name }) => getCombinedKey(key, page_name), ), (key) => key, @@ -197,6 +200,38 @@ async function pushMigration(migrationFilePath: string, apiUrl: string, authToke } }); + await writeFileAsync( + 'prevServerState.json', + JSON.stringify(serverStringMapByCombinedKey, null, 2), + 'utf8', + ); + + await writeFileAsync( + 'serverActions.json', + JSON.stringify(serverActions, null, 2), + 'utf8', + ); + + /* + const postPromise = await postLanguageStrings('en', [ + { + "action": "set", + "key": "ifrc", + "page_name": "countryPresence", + "value": "IFRC", + "hash": Md5.hashStr("IFRC"), + }, + ], apiUrl, authToken); + + const postResponseJson = await postPromise.json(); + + await writeFilePromisify( + 'serverResponse.json', + JSON.stringify(postResponseJson, null, 2), + 'utf8', + ); + */ + const postResponsePromises = serverActions.map( (serverAction) => postLanguageStrings( serverAction.language, @@ -206,7 +241,16 @@ async function pushMigration(migrationFilePath: string, apiUrl: string, authToke ) ); - await Promise.all(postResponsePromises); + const postResponses = await Promise.all(postResponsePromises); + const postJsonResponses = await Promise.all( + postResponses.map((response) => response.json()) + ); + + await writeFileAsync( + 'serverResponse.json', + JSON.stringify(postJsonResponses, null, 2), + 'utf8', + ); } export default pushMigration; diff --git a/app/scripts/translatte/commands/uploadJson.ts b/app/scripts/translatte/commands/uploadJson.ts new file mode 100644 index 0000000000..e379fc3b79 --- /dev/null +++ b/app/scripts/translatte/commands/uploadJson.ts @@ -0,0 +1,82 @@ +import { isDefined, listToGroupList } from "@togglecorp/fujs"; +import { isValidSourceStringItem, postLanguageStrings, readJsonSource } from "../utils"; +import { Language, SourceStringItem } from "../types"; + +async function postSourceStrings(language: Language, strings: SourceStringItem[], apiUrl: string, authToken: string) { + const response = await postLanguageStrings( + language, + strings.filter(isValidSourceStringItem).map( + (sourceItem) => ({ + action: 'set', + key: sourceItem.key, + page_name: sourceItem.page_name, + value: sourceItem.value, + hash: sourceItem.hash, + }) + ), + apiUrl, + authToken, + ); + + if (response.ok) { + console.info(`${language} pushed to server`); + } else { + console.info(response.status, response.statusText); + /* + const responseJson = await response.json(); + await writeFileAsync( + 'server-error.json', + JSON.stringify(responseJson, null, 2), + 'utf-8' + ); + */ + } +} + +async function uploadJson(jsonFilePath: string, apiUrl: string, authToken: string) { + const { content: strings } = await readJsonSource(jsonFilePath); + + const languageGroupedStringMap = listToGroupList( + strings, + ({ language }) => language, + ); + + if (isDefined(languageGroupedStringMap.en)) { + await postSourceStrings( + 'en', + languageGroupedStringMap.en, + apiUrl, + authToken, + ); + + } + + if (isDefined(languageGroupedStringMap.fr)) { + await postSourceStrings( + 'fr', + languageGroupedStringMap.fr, + apiUrl, + authToken, + ); + } + + if (isDefined(languageGroupedStringMap.es)) { + await postSourceStrings( + 'es', + languageGroupedStringMap.es, + apiUrl, + authToken, + ); + } + + if (isDefined(languageGroupedStringMap.ar)) { + await postSourceStrings( + 'ar', + languageGroupedStringMap.ar, + apiUrl, + authToken, + ); + } +} + +export default uploadJson; diff --git a/app/scripts/translatte/main.ts b/app/scripts/translatte/main.ts index 3a2c3cfcbe..002b666ca6 100644 --- a/app/scripts/translatte/main.ts +++ b/app/scripts/translatte/main.ts @@ -12,6 +12,8 @@ import exportMigration from './commands/exportMigration'; import { join, basename } from 'path'; import pushMigration from './commands/pushMigration'; import importExcel from './commands/importExcel'; +import exportStrings from './commands/exportStrings'; +import uploadJson from './commands/uploadJson'; const currentDir = cwd(); @@ -84,12 +86,12 @@ yargs(hideBin(process.argv)) }, ) .command( - 'apply-migrations ', + 'apply-migrations ', 'Apply migrations', (yargs) => { - yargs.positional('MIGRATION_FILE_PATH', { + yargs.positional('SOURCE_DIR', { type: 'string', - describe: 'Find the migration file on MIGRATION_FILE_PATH', + describe: 'Find the migration files on SOURCE_DIR', }); yargs.options({ 'dry-run': { @@ -101,27 +103,31 @@ yargs(hideBin(process.argv)) type: 'string', description: 'The file after which the migration will be applied', }, - 'source': { + 'server-strings': { type: 'string', description: 'The source file to which migration is applied', demandOption: true, }, - 'destination': { + 'output-dir': { type: 'string', - description: 'The file where new source file is saved', + description: 'Directory where new file is saved', demandOption: true, }, }); }, async (argv) => { console.warn(argv); + const destinationFilePath = join( + argv.outputDir as string, + `migrated-strings-${new Date().getTime()}.json` + ); + await applyMigrations( - currentDir, - argv.SOURCE_FILE as string, - argv.DESTINATION_FILE as string, - argv.MIGRATION_FILE_PATH as string, + argv.SOURCE_DIR as string, + argv.serverStrings as string, + destinationFilePath, ['es', 'ar', 'fr'], - argv.lastMigration as (string | undefined), + argv.lastMigration as string, argv.dryRun as (boolean | undefined), ); }, @@ -185,6 +191,29 @@ yargs(hideBin(process.argv)) ); }, ) + .command( + 'export-strings ', + 'Export strings from the server to a json file', + (yargs) => { + yargs.positional('SERVER_URL', { + type: 'string', + describe: 'URL from which strings are to be fetched', + }); + yargs.positional('OUTPUT_DIR', { + type: 'string', + describe: 'Directory where the output xlsx should be saved', + }); + }, + async (argv) => { + const outputDir = argv.OUTPUT_DIR as string; + const serverUrl = argv.SERVER_URL as string; + + await exportStrings( + serverUrl, + outputDir, + ); + }, + ) .command( 'push-migration ', 'Push migration file to the server', @@ -225,22 +254,61 @@ yargs(hideBin(process.argv)) describe: 'Find the import file on IMPORT_FILE_PATH', }); yargs.options({ + /* 'auth-token': { type: 'string', describe: 'Authentication token to access the API server', require: true, }, + */ 'api-url': { type: 'string', describe: 'URL for the API server', require: true, - } + }, + 'generated-string': { + type: 'string', + describe: 'file for generated strings', + require: true, + }, }); }, async (argv) => { const importFilePath = (argv.IMPORT_FILE_PATH as string); await importExcel( + importFilePath, + argv.apiUrl as string, + argv.generatedString as string, + // argv.authToken as string, + ); + }, + ) + .command( + 'upload-json ', + 'Upload to server from json file', + (yargs) => { + yargs.positional('FILE_PATH', { + type: 'string', + describe: 'Find the json file on FILE_PATH', + }); + yargs.options({ + 'auth-token': { + type: 'string', + describe: 'Authentication token to access the API server', + require: true, + }, + 'api-url': { + type: 'string', + describe: 'URL for the API server', + require: true, + }, + }); + }, + async (argv) => { + const importFilePath = (argv.FILE_PATH as string); + + await uploadJson( importFilePath, argv.apiUrl as string, argv.authToken as string, diff --git a/app/scripts/translatte/types.ts b/app/scripts/translatte/types.ts index 839200d3a3..47dddef316 100644 --- a/app/scripts/translatte/types.ts +++ b/app/scripts/translatte/types.ts @@ -47,11 +47,21 @@ export interface MigrationFileContent { actions: MigrationActionItem[], } +export type Language = 'en' | 'fr' | 'es' | 'ar' + +export interface StringItem { + key: string; + namespace: string; + language: Language; + value: string; + hash: string; +} + export interface SourceStringItem { hash: string; // id: string; key: string; - language: string; + language: Language; page_name: string; value: string; } @@ -61,4 +71,3 @@ export interface SourceFileContent { strings: SourceStringItem[]; } -export type Language = 'en' | 'fr' | 'es' | 'ar' diff --git a/app/scripts/translatte/utils.ts b/app/scripts/translatte/utils.ts index 56b7f0bc02..0cd9b5cc29 100644 --- a/app/scripts/translatte/utils.ts +++ b/app/scripts/translatte/utils.ts @@ -9,19 +9,21 @@ import { mapToList, unique, difference, + isTruthyString, } from '@togglecorp/fujs'; import { TranslationFileContent, MigrationFileContent, - SourceFileContent, + // SourceFileContent, Language, ServerActionItem, + SourceStringItem, } from './types'; -export const readFilePromisify = promisify(readFile); -export const writeFilePromisify = promisify(writeFile); -export const unlinkPromisify = promisify(unlink); +export const readFileAsync = promisify(readFile); +export const writeFileAsync = promisify(writeFile); +export const unlinkAsync = promisify(unlink); export const glob = fg.glob; // Utilities @@ -222,6 +224,35 @@ export function removeUndefinedKeys(itemFromArgs: T) { return item; } +export async function getMigrationFilesAttrsFromDir(dir: string) { + const fullPath = join(dir, '[0-9]+-[0-9]+.json'); + const files = await glob(fullPath, { ignore: ['node_modules'], absolute: true }); + + interface MigrationFileAttrs { + migrationName: string; + fileName: string; + num: string; + timestamp: string; + } + + const migrationFiles = files + .map((file): MigrationFileAttrs | undefined => { + const migrationName = basename(file); + const attrs = migrationName.match(/(?[0-9]+)-(?[0-9]+)/)?.groups as (Omit | undefined) + if (attrs) { + return { + ...attrs, + migrationName, + fileName: file, + } + } + return undefined; + }) + .filter(isDefined) + .sort((a, b) => a.migrationName.localeCompare(b.migrationName)); + return migrationFiles; +} + export async function getMigrationFilesAttrs(basePath: string, pathName: string) { const fullPath = isAbsolute(pathName) ? join(pathName, '[0-9]+-[0-9]+.json') @@ -270,7 +301,7 @@ export async function getTranslationFileNames(basePath: string, pathNames: strin export async function readJsonFilesContents(fileNames: string[]) { const contentsPromise = fileNames.map(async (fileName) => { - const fileDescriptor = await readFilePromisify(fileName); + const fileDescriptor = await readFileAsync(fileName); try { const content = JSON.parse(fileDescriptor.toString()); return { @@ -317,17 +348,20 @@ export async function readMigrations(fileNames: string[]) { return fileContents as { file: string, content: MigrationFileContent }[]; } -export async function readSource(fileName: string) { +export async function readJsonSource(fileName: string) { const fileContents = await readJsonFilesContents([fileName]); // TODO: validate the schema for content return fileContents[0] as { - file: string, content: SourceFileContent + file: string, + // TODO: update test for this change + // content: SourceFileContent + content: SourceStringItem[], }; } export async function removeFiles(files: string[]) { const removePromises = files.map(async (file) => ( - unlinkPromisify(file) + unlinkAsync(file) )); await Promise.all(removePromises); } @@ -345,16 +379,24 @@ export function resolveUrl(from: string, to: string) { return resolvedUrl.toString(); } -export async function fetchLanguageStrings(language: Language, apiUrl: string, authToken: string) { +export async function fetchLanguageStrings(language: Language, apiUrl: string, authToken?: string) { const endpoint = resolveUrl(apiUrl, language); + + const defaultHeaders = { + 'Accept': 'application/json' + } + + const headers = isDefined(authToken) + ? ({ + ...defaultHeaders, + 'Authorization': `Token ${authToken}`, + }) : defaultHeaders; + const promise = fetch( endpoint, { method: 'GET', - headers: { - 'Authorization': `Token ${authToken}`, - 'Accept': 'application/json' - } + headers, } ); @@ -362,7 +404,8 @@ export async function fetchLanguageStrings(language: Language, apiUrl: string, a } export async function postLanguageStrings(language: Language, actions: ServerActionItem[], apiUrl: string, authToken: string) { - const endpoint = resolveUrl(apiUrl, language); + const endpoint = resolveUrl(apiUrl, `${language}/bulk-action/`); + console.info(endpoint); const promise = fetch( endpoint, { @@ -378,3 +421,56 @@ export async function postLanguageStrings(language: Language, actions: ServerAct return promise; } +const languages: Language[] = ['en', 'fr', 'es', 'ar']; +const validLanguageMap = listToMap( + languages, + (language) => language, + () => true, +); + +export function isValidLanguage(language: unknown): language is Language { + return isDefined(language) + && typeof language === 'string' + && isTruthyString(language) + && validLanguageMap[language]; +} + +export function isValidSourceStringItem(stringItem: Partial): stringItem is SourceStringItem { + return isDefined(stringItem.key) + && isDefined(stringItem.page_name) + && isTruthyString(stringItem.value) + && isTruthyString(stringItem.hash) + && isValidLanguage(stringItem.language) +} + +export async function fetchAllServerStrings(apiUrl: string, authToken?: string) { + const responsePromises = languages.map( + (language) => fetchLanguageStrings(language, apiUrl, authToken) + ); + + const responses = await Promise.all(responsePromises); + + const languageJsonPromises = responses.map( + (response) => response.json() + ); + + const languageStrings = await Promise.all(languageJsonPromises); + + const serverStrings = languageStrings.flatMap( + (languageString) => { + const language: Language = languageString.code; + + const strings: SourceStringItem[] = languageString.strings.map( + (string: Omit) => ({ + ...string, + language, + }) + ) + + return strings.filter(isValidSourceStringItem); + } + ); + + return serverStrings; +} + diff --git a/translationMigrations/000003-1712631208970.json b/translationMigrations/000003-1712631208970.json index 7592612241..fb7a2d8b5c 100644 --- a/translationMigrations/000003-1712631208970.json +++ b/translationMigrations/000003-1712631208970.json @@ -701,4 +701,4 @@ "value": "Note: You can only upload upto 10 documents." } ] -} \ No newline at end of file +}