INTO-CPS-Association/DTaaS

View on GitHub
client/src/preview/util/fileHandler.ts

Summary

Maintainability
A
1 hr
Test Coverage
/* eslint-disable no-restricted-syntax */
/* eslint-disable no-await-in-loop */

import { FileState } from 'preview/store/file.slice';
import GitlabInstance from './gitlab';
import { IFile } from './ifile';
import { FileType } from './DTAssets';

const COMMON_LIBRARY_PROJECT_ID = 3;

export function isValidFileType(
  item: { type: string; name: string; path: string },
  fileType: FileType,
): boolean {
  const typeChecks = {
    [FileType.DESCRIPTION]: item.type === 'blob' && item.name.endsWith('.md'),
    [FileType.CONFIGURATION]:
      item.type === 'blob' && /\.(json|yml|yaml)$/.test(item.name),
    [FileType.LIFECYCLE]:
      item.type === 'blob' && item.path.includes('/lifecycle/'),
  };

  return typeChecks[fileType];
}

export function isImageFile(fileName: string): boolean {
  const imageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.svg'];
  return imageExtensions.some((ext) => fileName.toLowerCase().endsWith(ext));
}

class FileHandler implements IFile {
  public name: string;

  public gitlabInstance: GitlabInstance;

  constructor(name: string, gitlabInstance: GitlabInstance) {
    this.name = name;
    this.gitlabInstance = gitlabInstance;
  }

  async createFile(
    file: FileState | { name: string; content: string; isNew: boolean },
    filePath: string,
    commitMessage: string,
    commonProject?: boolean,
  ): Promise<void> {
    const projectToUse = commonProject
      ? COMMON_LIBRARY_PROJECT_ID
      : this.gitlabInstance.projectId;
    await this.gitlabInstance.api.RepositoryFiles.create(
      projectToUse!,
      `${filePath}/${file.name}`,
      'main',
      file.content,
      commitMessage,
    );
  }

  async updateFile(
    filePath: string,
    updatedContent: string,
    commitMessage: string,
  ): Promise<void> {
    await this.gitlabInstance.api.RepositoryFiles.edit(
      this.gitlabInstance.projectId!,
      filePath,
      'main',
      updatedContent,
      commitMessage,
    );
  }

  async deleteDT(digitalTwinPath: string): Promise<void> {
    await this.gitlabInstance.api.RepositoryFiles.remove(
      this.gitlabInstance.projectId!,
      digitalTwinPath,
      'main',
      `Removing ${this.name} digital twin`,
    );
  }

  async getFileContent(filePath: string, isPrivate?: boolean): Promise<string> {
    const projectToUse =
      isPrivate === false
        ? COMMON_LIBRARY_PROJECT_ID
        : this.gitlabInstance.projectId;

    const response = await this.gitlabInstance.api.RepositoryFiles.show(
      projectToUse!,
      filePath,
      'main',
    );
    return atob(response.content);
  }

  async getFileNames(fileType: FileType): Promise<string[]> {
    const pathMap = {
      [FileType.DESCRIPTION]: `digital_twins/${this.name}`,
      [FileType.CONFIGURATION]: `digital_twins/${this.name}`,
      [FileType.LIFECYCLE]: `digital_twins/${this.name}/lifecycle`,
    };

    try {
      const response =
        await this.gitlabInstance.api.Repositories.allRepositoryTrees(
          this.gitlabInstance.projectId!,
          {
            path: pathMap[fileType],
            recursive: fileType === FileType.LIFECYCLE,
          },
        );

      return response
        .filter((item) => isValidFileType(item, fileType))
        .map((file) => file.name);
    } catch {
      return [];
    }
  }

  async getLibraryFileNames(
    filePath: string,
    isPrivate: boolean,
  ): Promise<string[]> {
    const projectToUse = isPrivate
      ? this.gitlabInstance.projectId
      : COMMON_LIBRARY_PROJECT_ID;

    try {
      const response =
        await this.gitlabInstance.api.Repositories.allRepositoryTrees(
          projectToUse!,
          {
            path: filePath,
            recursive: false,
          },
        );

      const fileNames: string[] = [];
      for (const file of response) {
        if (file.type === 'tree') {
          const nestedFiles = await this.getLibraryFileNames(
            `${filePath}/${file.name}`,
            isPrivate,
          );
          fileNames.push(
            ...nestedFiles.map((nestedFile) => `${file.name}/${nestedFile}`),
          );
        } else if (!isImageFile(file.name) && !file.name.endsWith('.fmu')) {
          fileNames.push(file.name);
        }
      }

      return fileNames;
    } catch {
      return [];
    }
  }

  async getLibraryConfigFileNames(
    filePath: string,
    isPrivate: boolean,
  ): Promise<string[]> {
    const projectToUse = isPrivate
      ? this.gitlabInstance.projectId
      : COMMON_LIBRARY_PROJECT_ID;

    const shouldBeRecursive = filePath.includes('common/');

    try {
      const response =
        await this.gitlabInstance.api.Repositories.allRepositoryTrees(
          projectToUse!,
          {
            path: filePath,
            recursive: shouldBeRecursive,
          },
        );

      return response
        .filter((item) => isValidFileType(item, FileType.CONFIGURATION))
        .map((file) => file.name);
    } catch (_error) {
      return [];
    }
  }

  async getFolders(path: string): Promise<string[]> {
    try {
      const response =
        await this.gitlabInstance.api.Repositories.allRepositoryTrees(
          this.gitlabInstance.projectId!,
          { path, recursive: false },
        );

      return response
        .filter((item: { type: string }) => item.type === 'tree')
        .map((folder: { path: string }) => folder.path);
    } catch (_error) {
      return [];
    }
  }
}

export default FileHandler;