/ src / scripts / crowdin / source-files / fetchAndSaveFileIds.ts
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