fetchAndSaveFileIds.ts
1 import fs from "fs" 2 import path from "path" 3 4 import { ResponseObject, SourceFilesModel } from "@crowdin/crowdin-api-client" 5 6 import { 7 CROWDIN_API_MAX_LIMIT, 8 CROWDIN_PROJECT_ID, 9 } from "../../../lib/constants" 10 import crowdinClient from "../api-client/crowdinClient" 11 12 const { sourceFilesApi } = crowdinClient 13 14 interface FileItem { 15 id: number 16 path: string 17 } 18 19 async function fetchFileIdsForDirectory( 20 directoryId: number 21 ): Promise<FileItem[]> { 22 try { 23 const response = await sourceFilesApi.listProjectFiles(CROWDIN_PROJECT_ID, { 24 limit: CROWDIN_API_MAX_LIMIT, 25 directoryId, 26 recursion: true, 27 }) 28 29 if (!response.data || !Array.isArray(response.data)) { 30 console.error(`Invalid response data structure.`, response.data) 31 return [] 32 } 33 34 return response.data 35 .map((item: ResponseObject<SourceFilesModel.File>): FileItem => ({ 36 id: item.data.id, 37 path: item.data.path, 38 })) 39 .filter((file: FileItem) => file.path.endsWith('.md')); // filter out non-md files 40 } catch (error: unknown) { 41 if (error instanceof Error) { 42 console.error( 43 `There was a problem with the fetch operation for directory ${directoryId}: ${error.message}` 44 ) 45 } 46 return [] 47 } 48 } 49 50 async function fetchFileIdsForMultipleDirectories( 51 directoryIds: number[] 52 ): Promise<FileItem[] | undefined> { 53 const promises = directoryIds.map(fetchFileIdsForDirectory) 54 const results = await Promise.allSettled(promises) 55 56 const successfulResults: FileItem[][] = results 57 .filter( 58 (result): result is PromiseFulfilledResult<FileItem[]> => 59 result.status === "fulfilled" 60 ) 61 .map((result) => result.value) 62 63 if (successfulResults.length === 0) { 64 console.log("No successful fetch operations.") 65 return 66 } 67 68 const combinedData: FileItem[] = successfulResults 69 .flat() 70 .map(transformResponseData) 71 72 return combinedData 73 } 74 75 // Convert path by removing everything before the second forward slash 76 // Before: '/28) Developer Tutorials IV/developers/tutorials/testing-erc-20-tokens-with-waffle/index.md' 77 // After: '/developers/tutorials/testing-erc-20-tokens-with-waffle/index.md' 78 function transformResponseData(item: FileItem): FileItem { 79 const pathSegments = item.path.split("/") 80 const newPath = "/" + pathSegments.slice(2).join("/") 81 82 return { 83 id: item.id, 84 path: newPath, 85 } 86 } 87 88 function saveFileIdsToJSON(combinedData: FileItem[]): void { 89 const dir = "./src/data/crowdin" 90 const outputFilePath = path.join(dir, "file-ids.json") 91 92 try { 93 // Do not overwrite the file if there's no data to save 94 if (combinedData.length === 0) { 95 console.log("No data to save. Keeping the existing data.") 96 return 97 } 98 99 // TODO: Remove if check is redundant (will this always follow getAndSaveDirectories.ts??) 100 fs.mkdirSync(dir, { recursive: true }) 101 fs.writeFileSync(outputFilePath, JSON.stringify(combinedData, null, 2)) 102 console.log(`File id data saved to ${outputFilePath}`) 103 } catch (error: unknown) { 104 if (error instanceof Error) { 105 console.error( 106 "There was a problem saving the data to the file:", 107 error.message 108 ) 109 } 110 } 111 } 112 113 async function fetchAndSaveFileIds(directoryIds: number[]): Promise<void> { 114 const transformedFileData = await fetchFileIdsForMultipleDirectories( 115 directoryIds 116 ) 117 118 if (transformedFileData) { 119 saveFileIdsToJSON(transformedFileData) 120 } else { 121 console.log("No data to save. Keeping the existing data.") 122 } 123 } 124 125 export default fetchAndSaveFileIds