File

src/security/fund.service.ts

Index

Properties
Methods

Constructor

constructor(fundModel: Model<FundDocument>)
Parameters :
Name Type Optional
fundModel Model<FundDocument> No

Methods

catch
catch(error)
Parameters :
Name Optional
error No
Returns : any
Async create
create(createFundDto: CreateFundDto, email: string)
Parameters :
Name Type Optional
createFundDto CreateFundDto No
email string No
Async findByIds
findByIds(undefined: literal type)
Parameters :
Name Type Optional
literal type No
Returns : Promise<Fund[]>
Async findByIdsAndInstrument
findByIdsAndInstrument(undefined: literal type)
Parameters :
Name Type Optional
literal type No
Returns : Promise<FundWithInstrument[]>
Async findFundsByFilter
findFundsByFilter(filterFundDto: FilterFundDto)
Parameters :
Name Type Optional
filterFundDto FilterFundDto No
Returns : Promise<FundWithInstrument | []>
Async findOne
findOne(id: string)
Parameters :
Name Type Optional
id string No
Returns : Promise<FundWithInstrument | LeanDocument>
Async fundFilesUploader
fundFilesUploader(fund)
Parameters :
Name Optional
fund No
Returns : unknown
Async getFundWithUnSignedUrl
getFundWithUnSignedUrl(fund: LeanDocument<FundDocument>)
Parameters :
Name Type Optional
fund LeanDocument<FundDocument> No
Returns : unknown
Async insertInstruments
insertInstruments(funds: LeanDocument<FundDocument>[])
Parameters :
Name Type Optional
funds LeanDocument<FundDocument>[] No
Returns : Promise<FundWithInstrument[]>
Async update
update(id: string, updateFundDto: UpdateFundDto)
Parameters :
Name Type Optional
id string No
updateFundDto UpdateFundDto No

Properties

Private Readonly fundWeb3
Type : FundWeb3Service
Decorators :
@Inject(FundWeb3Service)
Private Readonly instrumentService
Type : InstrumentService
Decorators :
@Inject(InstrumentService)
Private Readonly s3Service
Type : S3Service
import { InstrumentService } from "./instrument.service";
import { LeanDocument, Model } from "mongoose";
import { InjectModel } from "@nestjs/mongoose";
import {
  BadRequestException,
  Inject,
  Injectable,
  ServiceUnavailableException,
} from "@nestjs/common";

import type { CreateFundDto } from "./dto/create-fund.dto";
import type { UpdateFundDto } from "./dto/update-fund.dto";
import type { FilterFundDto } from "./dto/filter-fund.dto";

import { FundWeb3Service } from "./fund.web3.service";
import { S3Service } from "src/common/provider/storage/s3";
import { Fund, FundDocument } from "./schemas/fund.schema";

import { decodeBytes32String } from "ethers";
import { FundWithInstrument, InstrumentSubgraphAndDB } from "./fund.interface";
import { uploadCmsFileFields } from "src/common/utils";
import { IFile } from "src/common/interfaces";

@Injectable()
export class FundService {
  @Inject(FundWeb3Service)
  private readonly fundWeb3: FundWeb3Service;

  @Inject(InstrumentService)
  private readonly instrumentService: InstrumentService;

  private readonly s3Service: S3Service;
  constructor(
    @InjectModel(Fund.name) private readonly fundModel: Model<FundDocument>,
  ) {
    this.s3Service = new S3Service();
  }

  async create(
    createFundDto: CreateFundDto,
    email: string,
  ): Promise<FundDocument> {
    const fundWithFiles = await this.fundFilesUploader({
      ...createFundDto,
      creator: email,
    });
    const fund = new this.fundModel(fundWithFiles);
    await this.fundWeb3.addFund(fund.id, email);
    return fund.save();
  }

  async findByIds({
    ids,
    filterFundDto,
  }: {
    ids: string[];
    filterFundDto?: FilterFundDto;
  }): Promise<Fund[]> {
    try {
      const { limit, skip, orderDirection, selectors, ...restOfFilter } =
        filterFundDto ?? {};
      const funds = await this.fundModel
        .find(
          { _id: { $in: ids }, ...restOfFilter },
          selectors ? selectors.join(" ") : "",
        )
        .limit(limit)
        .skip(skip * limit)
        .sort({ created: orderDirection })
        .lean()
        .exec();
      return await Promise.all(
        funds.map((fund) => this.getFundWithUnSignedUrl(fund)),
      );
    } catch (error) {
      console.error(error);
      throw new BadRequestException("Wrong subscribedFunds Id", {
        cause: error,
      });
    }
  }
  async findByIdsAndInstrument({
    ids,
    filterFundDto,
  }: {
    ids: string[];
    filterFundDto?: FilterFundDto;
  }): Promise<FundWithInstrument[]> {
    try {
      const funds = await this.fundModel
        .find({ _id: { $in: ids }, ...filterFundDto })
        .lean()
        .exec();

      return await this.insertInstruments(funds);
    } catch (error) {
      console.error(error);
      throw new ServiceUnavailableException({
        cause: error,
      });
    }
  }
  async findFundsByFilter(
    filterFundDto: FilterFundDto,
  ): Promise<FundWithInstrument | LeanDocument<FundDocument>[]> {
    const { limit, skip, orderDirection, selectors, ...restOfFilter } =
      filterFundDto ?? {};

    const funds: LeanDocument<FundDocument>[] = await this.fundModel
      .find(restOfFilter, selectors ? selectors.join(" ") : "")
      .limit(limit)
      .skip(skip * limit)
      .sort({ created: orderDirection })
      .lean()
      .exec();
    return selectors
      ? await Promise.all(
          funds.map((fund) => this.getFundWithUnSignedUrl(fund)),
        )
      : await this.insertInstruments(funds);
  }

  async findOne(
    id: string,
  ): Promise<FundWithInstrument | LeanDocument<FundDocument>> {
    const fund = await this.fundModel.findById(id).lean().exec();
    if (!fund) {
      return null;
    }
    return await this.getFundWithUnSignedUrl(fund);
  }

  async update(
    id: string,
    updateFundDto: UpdateFundDto,
  ): Promise<FundDocument> {
    const cmsFields = [];
    if (updateFundDto.cmsFields) {
      cmsFields.push(...(await uploadCmsFileFields(updateFundDto.cmsFields)));
    }
    const uploadToS3: Record<string, string> = {};
    await Promise.all(
      Object.keys(updateFundDto).map(async (propertyKey) => {
        if (updateFundDto[propertyKey].base64String) {
          uploadToS3[propertyKey] = await this.s3Service.upload(
            updateFundDto[propertyKey] as IFile,
            `updated-${propertyKey}-${
              updateFundDto[propertyKey].originalname.split(".")[0]
            }-${new Date().getTime()}.${
              updateFundDto[propertyKey].originalname.split(".")[1]
            }`,
          );
        }
      }),
    );
    return await this.fundModel
      .findByIdAndUpdate(
        id,
        {
          ...updateFundDto,
          ...uploadToS3,
          cmsFields,
        },
        { new: true },
      )
      .exec();
  }
  async insertInstruments(
    funds: LeanDocument<FundDocument>[],
  ): Promise<FundWithInstrument[]> {
    return await Promise.all(
      funds.map(async (fund) => {
        const fundWithUrl = await this.getFundWithUnSignedUrl(fund);
        const [instrumentsSubgraph, instrumentsDb] = await Promise.all([
          this.fundWeb3.getInstrumentsByFundId(fund._id.toString()),
          this.instrumentService.findAllByFilter({
            fundId: fund._id.toString(),
          }),
        ]);

        const instruments: InstrumentSubgraphAndDB[] = instrumentsSubgraph.map(
          (subgraphInstrument) => {
            const dbInstrument = instrumentsDb.find(
              (currentInstrument) =>
                currentInstrument._id.toString() ===
                decodeBytes32String(subgraphInstrument.id),
            );
            return {
              dbInstrument,
              subgraphInstrument,
            };
          },
        );
        return { ...fundWithUrl, instruments };
      }),
    );
  }

  async fundFilesUploader(fund: CreateFundDto & { creator: string }) {
    const [
      logoFile,
      brochureFile,
      keyDocumentsFile,
      extraDocumentsFile,
      partiesFile,
      promotionVideoFile,
      strategyFile,
      performanceFile,
    ] = await Promise.all([
      this.s3Service.upload(
        fund.logoFile,
        `logo-${
          fund.logoFile.originalname.split(".")[0]
        }-${new Date().getTime()}-${fund.creator}.${
          fund.logoFile.originalname.split(".")[1]
        }`,
      ),
      this.s3Service.upload(
        fund.brochureFile,
        `brochure-${
          fund.brochureFile.originalname.split(".")[0]
        }-${new Date().getTime()}-${fund.creator}.${
          fund.brochureFile.originalname.split(".")[1]
        }`,
      ),
      this.s3Service.upload(
        fund.keyDocumentsFile,
        `keyDocumentsFile-${
          fund.keyDocumentsFile.originalname.split(".")[0]
        }-${new Date().getTime()}-${fund.creator}.${
          fund.keyDocumentsFile.originalname.split(".")[1]
        }`,
      ),
      this.s3Service.upload(
        fund.extraDocumentsFile,
        `extraDocumentsFile-${
          fund.extraDocumentsFile.originalname.split(".")[0]
        }-${new Date().getTime()}-${fund.creator}.${
          fund.extraDocumentsFile.originalname.split(".")[1]
        }`,
      ),
      this.s3Service.upload(
        fund.partiesFile,
        `partiesFile-${
          fund.partiesFile.originalname.split(".")[0]
        }-${new Date().getTime()}-${fund.creator}.${
          fund.partiesFile.originalname.split(".")[1]
        }`,
      ),
      this.s3Service.upload(
        fund.promotionVideoFile,
        `promotionVideoFile-${
          fund.promotionVideoFile.originalname.split(".")[0]
        }-${new Date().getTime()}-${fund.creator}.${
          fund.promotionVideoFile.originalname.split(".")[1]
        }`,
      ),
      this.s3Service.upload(
        fund.strategy.file,
        `strategyDiagram-${
          fund.strategy.file.originalname.split(".")[0]
        }-${new Date().getTime()}-${fund.creator}.${
          fund.strategy.file.originalname.split(".")[1]
        }`,
      ),
      this.s3Service.upload(
        fund.performance.file,
        `performanceFile-${
          fund.performance.file.originalname.split(".")[0]
        }-${new Date().getTime()}-${fund.creator}.${
          fund.performance.file.originalname.split(".")[1]
        }`,
      ),
    ]);
    const fundWithUploadedFiles: Fund = {
      ...fund,
      logoFile,
      brochureFile,
      keyDocumentsFile,
      extraDocumentsFile,
      partiesFile,
      promotionVideoFile,
      strategy: {
        submitter: fund.creator,
        fileKey: strategyFile,
        data: fund.strategy.data,
      },
      performance: {
        submitter: fund.creator,
        fileKey: performanceFile,
        data: fund.performance.data,
      },
    };
    return fundWithUploadedFiles;
  }
  async getFundWithUnSignedUrl(fund: LeanDocument<FundDocument>) {
    const filesArray = await Promise.all(
      Object.keys(fund)
        .filter(
          (propertyKey) =>
            fund[propertyKey].fileKey ||
            (String(fund[propertyKey]).includes(".") &&
              String(fund[propertyKey]).includes("-")),
        )
        .map(async (fileOrReportKey) => {
          if (fund[fileOrReportKey].fileKey) {
            const fileKey = await this.s3Service.getFileByKey(
              fund[fileOrReportKey].fileKey,
            );
            return {
              [fileOrReportKey]: {
                ...fund[fileOrReportKey],
                fileKey,
              },
            };
          }
          const fileKey = await this.s3Service.getFileByKey(
            fund[fileOrReportKey],
          );
          return { [fileOrReportKey]: fileKey };
        }),
    );
    const filesObject = Object.assign({}, ...filesArray);
    return {
      ...fund,
      ...filesObject,
    };
  }
  catch(error) {
    console.error(error);
    return null;
  }
}

results matching ""

    No results matching ""