File

src/metrics/metrics.service.ts

Index

Properties
Methods

Constructor

constructor(prisma: PrismaService, projectService: ProjectService, modelService: ModelService, temporalProvider: TemporalProvider)
Parameters :
Name Type Optional
prisma PrismaService No
projectService ProjectService No
modelService ModelService No
temporalProvider TemporalProvider No

Methods

Async fetchMetricEntries
fetchMetricEntries(query: literal type)
Parameters :
Name Type Optional
query literal type No
Returns : unknown
Async fetchMetricForSources
fetchMetricForSources(query: literal type)
Parameters :
Name Type Optional
query literal type No
Returns : unknown
Async fetchMetricsData
fetchMetricsData(query: literal type)
Parameters :
Name Type Optional
query literal type No
Returns : unknown
Async fetchRawMetricsData
fetchRawMetricsData(query: literal type)

Fetches raw metrics data for the given metric ID and number of days.

Parameters :
Name Type Optional Description
query literal type No
  • An object containing the metric ID and number of days to fetch data for.
Returns : unknown

A promise that resolves to an array of raw metrics data.

Async getMetric
getMetric(metricId: number)

Finds and returns the metric with the specified ID.

Parameters :
Name Type Optional Description
metricId number No
  • The ID of the metric to find.
Returns : Promise<Metrics>

A Promise that resolves to the found metric object.

Async getMetricByIdentifier
getMetricByIdentifier(projectId: number, sourceId: number, sourceType: MetricSources, name: string, type: string)
Parameters :
Name Type Optional
projectId number No
sourceId number No
sourceType MetricSources No
name string No
type string No
Returns : unknown
Async getSource
getSource(sourceType: string, sourceId: number)
Parameters :
Name Type Optional
sourceType string No
sourceId number No
Returns : unknown
Async saveMetricsData
saveMetricsData(metrics: MetricDataObject[])
Parameters :
Name Type Optional
metrics MetricDataObject[] No
Returns : unknown
Async updateMetricsMetadata
updateMetricsMetadata(id: number, metadata: literal type)

Updates the metadata of a metric with the given ID.

Parameters :
Name Type Optional Description
id number No
  • The ID of the metric to update.
metadata literal type No
  • An object containing the metadata to update.
Returns : unknown

A promise that resolves to an instance of the updated metric.

Async upsertMetricEntry
upsertMetricEntry(query: literal type)
Parameters :
Name Type Optional
query literal type No
Returns : unknown

Properties

Private sourceServiceMap
Type : object
Default value : { [MetricSources.MODEL]: this.modelService, }
import { PrismaService } from "../common/prisma/prisma.service";
import { Injectable } from "@nestjs/common";
import { MetricDataObject, MetricsDto } from "./dto/metrics.dto";
import { ProjectService } from "../project/project.service";
import { MetricSources, Metrics, MetricTrend, MetricStatus } from "./entities/metrics.entity";
import { ModelService } from "../models/model.service";
import { getUuidSlug, groupBy } from "../common/helper";
import { TemporalProvider } from "../providers/temporal/temporal.provider";

@Injectable()
export class MetricsService {
  constructor(
    private prisma: PrismaService,
    private projectService: ProjectService,
    private modelService: ModelService,
    private temporalProvider: TemporalProvider,
  ) {}

  private sourceServiceMap = {
    [MetricSources.MODEL]: this.modelService,
  };

  async saveMetricsData(metrics: MetricDataObject[]) {
    // if metricId and timestamp already exist delete the existing data
    const metricIds = [...new Set(metrics.map((metric) => metric.metricId))];

    await this.prisma.metricsData.deleteMany({
      where: {
        metricId: { in: metricIds },
        timestamp: { in: metrics.map((metric) => metric.timestamp) },
      },
    });

    // if duplicate timestamps then keep only the last value
    const uniqueMetrics = metrics.filter(
      (metric, index, self) =>
        index ===
        self.findIndex(
          (t) =>
            t.metricId === metric.metricId &&
            t.timestamp.getTime() === metric.timestamp.getTime(),
        ),
    );

    const batchPayload = await this.prisma.metricsData.createMany({
      data: uniqueMetrics,
    });

    return {
      count: batchPayload.count,
      rows: uniqueMetrics,
    };
  }

  /**
   * Finds and returns the metric with the specified ID.
   * @param {number} metricId - The ID of the metric to find.
   * @returns {Promise<Metrics>} A Promise that resolves to the found metric object.
   * @throws {Error} If there are issues finding the metric.
   */
  async getMetric(metricId: number): Promise<Metrics> {
    const metric = await this.prisma.metrics.findFirst({
      where: { id: metricId },
    });
    return new Metrics({
      ...metric,
      trend: metric.trend as MetricTrend,
    });
  }

  async getMetricByIdentifier(
    projectId: number,
    sourceId: number,
    sourceType: MetricSources,
    name: string,
    type: string,
  ) {
    const metric = await this.prisma.metrics.findUniqueOrThrow({
      where: {
        metricIdentifier: {
          projectId: projectId,
          sourceId: sourceId,
          sourceType: sourceType,
          name: name,
          type: type,
        },
      },
    });
    return new Metrics({
      ...metric,
      trend: metric.trend as MetricTrend,
    });
  }

  async getSource(sourceType: string, sourceId: number) {
    if (!this.sourceServiceMap.hasOwnProperty(sourceType)) {
      throw new Error(`Unsupported metrics source type - ${sourceType}`);
    }
    const sourceService = this.sourceServiceMap[sourceType];
    return sourceService.findOrThrow(sourceId);
  }

  async fetchMetricEntries(query: {
    projectId?: number;
    sourceId?: number;
    sourceType?: MetricSources;
    name?: string;
    type?: string;
  }) {
    const result = await this.prisma.metrics.findMany({
      where: {
        ...query,
        status: MetricStatus.ACTIVE
      },
    });
    return result;
  }

  async fetchMetricForSources(query: {
    sourceType: MetricSources;
    sourceIds: number[];
  }) {
    const result = await this.prisma.metrics.findMany({
      where: {
        sourceType: query.sourceType,
        sourceId: { in: query.sourceIds },
        status: MetricStatus.ACTIVE,
      },
    });
    return result;
  }

  async upsertMetricEntry(query: {
    projectId: number;
    sourceId: number;
    sourceType: MetricSources;
    name: string;
    type: string;
  }) {
    const metric = new MetricsDto({
      projectId: query.projectId,
      sourceId: query.sourceId,
      sourceType: query.sourceType,
      name: query.name,
      type: query.type,
    });

    await this.projectService.findOrThrow(metric.projectId);
    await this.getSource(metric.sourceType, +metric.sourceId);

    const result = await this.prisma.metrics.upsert({
      where: {
        metricIdentifier: metric,
      },
      create: { ...metric, slug: getUuidSlug() },
      update: {},
    });
    return result;
  }

  async fetchMetricsData(query: {
    name?: string;
    projectId?: number;
    source?: {
      id: number;
      type: MetricSources;
    };
    type?: string;
    startTime?: Date;
    endTime?: Date;
  }) {
    const filter = {
      name: query.name,
      projectId: query.projectId,
      sourceType: query.source ? query.source.type : undefined,
      sourceId: query.source ? query.source.id : undefined,
      type: query.type,
    };

    const metricEntries = await this.prisma.metrics.findMany({
      where: {
        ...filter,
        status: MetricStatus.ACTIVE,
      },
    });

    const metricsData = await this.prisma.metricsData.findMany({
      where: {
        metricId: { in: metricEntries.map((metric) => metric.id) },
        timestamp: { gte: query.startTime, lte: query.endTime },
      },
    });
    const groupedMetrics = groupBy(metricsData, ({ metricId }) => metricId);
    const targetIds = metricsData.map((metric) => metric.id);

    const result = {
      targetIds: targetIds,
      metricEntries: metricEntries,
      metricsData: groupedMetrics,
    };
    return result;
  }

  /**
   * Fetches raw metrics data for the given metric ID and number of days.
   * @param query - An object containing the metric ID and number of days to fetch data for.
   * @returns A promise that resolves to an array of raw metrics data.
   */
  async fetchRawMetricsData(query: {
    metricId: number;
    startTime: Date;
    endTime: Date;
  }) {
    const metricData = await this.prisma.metricsData.findMany({
      where: {
        metricId: query.metricId,
        timestamp: { gte: query.startTime, lte: query.endTime },
      },
      orderBy: {
        timestamp: "desc",
      },
    });

    return metricData;
  }

  /**
   * Updates the metadata of a metric with the given ID.
   *
   * @param id - The ID of the metric to update.
   * @param metadata - An object containing the metadata to update.
   * @param metadata.trend - (Optional) The new trend of the metric.
   * @param metadata.trendValue - (Optional) The new trend value of the metric.
   * @param metadata.description - (Optional) The new description of the metric.
   * @returns A promise that resolves to an instance of the updated metric.
   */
  async updateMetricsMetadata(
    id: number,
    metadata: {
      trend?: MetricTrend;
      description?: string;
      trendValue?: number;
      status?: MetricStatus;
    },
  ) {
    const { trend, description, trendValue, status } = metadata;
    const metric = await this.prisma.metrics.update({
      where: { id: id },
      data: {
        trend,
        description,
        trendValue,
        status,
      },
    });

    return new Metrics({
      ...metric,
      trend: metric.trend as MetricTrend,
    });
  }
}

results matching ""

    No results matching ""