File

src/cost/cost.controller.ts

Prefix

cost

Description

Controller responsible for handling requests related to turo cost operations.

Index

Methods

Methods

canRegisterCostEndpoints
canRegisterCostEndpoints(request, projectId: string)
Decorators :
@Get('/:projectId/endpoints/hasCreatePermission')

Check if user token in request has permission to register an cost

Parameters :
Name Type Optional
request No
projectId string No
Returns : any

boolean

Async deleteCostEndpoint
deleteCostEndpoint(id: number, request)
Decorators :
@Delete('/endpoints/:id')

Delete registered cost endpoint for a project

Parameters :
Name Type Optional
id number No
request No
Returns : unknown

deleted endpoint or throws error

getConfiguredCosts
getConfiguredCosts(providerName: string)
Decorators :
@Get('/providers/:providerName/llm/getConfiguredCosts')
Parameters :
Name Type Optional
providerName string No
Returns : any
Async getCost
getCost(projectID: number, query: QueryCostDto, request)
Decorators :
@Get('/:projectID')

Function to get cost data based on input query params

Parameters :
Name Type Optional
projectID number No
query QueryCostDto No
request No
Returns : unknown

cost data for window

Async getCostEndpointDetails
getCostEndpointDetails(id: number)
Decorators :
@Get('/endpoints/:id')

Get endpoint details by endpoint id without credentials

Parameters :
Name Type Optional
id number No
Returns : unknown

cost endpoint

Async getCostEndpoints
getCostEndpoints(projectID: string, request)
Decorators :
@Get('/:projectID/endpoints')

List registered cost endpoints for input project ID

Parameters :
Name Type Optional
projectID string No
request No
Returns : unknown

List of cost endpoints with permissions on update/delete

Async getCostOverview
getCostOverview(request)
Decorators :
@Get('/overview')

List projectwise cost incurred till date will only return cost for project user has access to

Parameters :
Name Optional
request No
Returns : unknown
getResourcesByTags
getResourcesByTags(fetchResourceDto: FetchResourcesDto)
Decorators :
@Post('/endpoints/getResourcesByTags')

Get resources based on tagList filter

Parameters :
Name Type Optional
fetchResourceDto FetchResourcesDto No
Returns : any

Get tags api response

Async getResourcesWithTagsForConnection
getResourcesWithTagsForConnection(id: number, tag: string)
Decorators :
@Get('/endpoints/:id/getResourcesForTags/:tag')

Get resources from connection based on input connection id and tag. API can only filter one tag at a time due to aws provider limitation

Parameters :
Name Type Optional Description
id number No

connection id

tag string No

tag key value pair eg: key:Value1

Returns : unknown

Get resource by tags api response

getTagKeys
getTagKeys(fetchCostTagsDto: FetchCostTagsDto)
Decorators :
@Post('endpoints/getTagKeys')
Parameters :
Name Type Optional
fetchCostTagsDto FetchCostTagsDto No
Returns : any
Async getTagKeysForConnection
getTagKeysForConnection(id: number)
Decorators :
@Post('endpoints/:id/getTagKeys')
Parameters :
Name Type Optional
id number No
Returns : unknown
getTagsData
getTagsData(fetchCostTagsDto: FetchCostTagsDto)
Decorators :
@Post('/endpoints/getTagsData')

Get tags from connection based on input URL and credentials

Parameters :
Name Type Optional
fetchCostTagsDto FetchCostTagsDto No
Returns : any

Get tags api response

Async getTagsForConnection
getTagsForConnection(id: number)
Decorators :
@Get('/endpoints/:id/getTags')

Get tags from connection based on input connection id

Parameters :
Name Type Optional Description
id number No

connection id

Returns : unknown

Get tags api response

getTagValuesForKey
getTagValuesForKey(fetchCostTagsDto: FetchCostTagsDto)
Decorators :
@Post('endpoints/getTagValuesForKey')
Parameters :
Name Type Optional
fetchCostTagsDto FetchCostTagsDto No
Returns : any
Async getTagValuesForKeyForConnection
getTagValuesForKeyForConnection(id: number, tag: string)
Decorators :
@Post('endpoints/:id/getTagValuesForKey/:tag')
Parameters :
Name Type Optional
id number No
tag string No
Returns : unknown
listModels
listModels(providerName: string)
Decorators :
@Get('/providers/:providerName/llm/models')
Parameters :
Name Type Optional
providerName string No
Returns : any
listProviders
listProviders()
Decorators :
@Get('/providers')
Returns : any
Async pushCostMetrics
pushCostMetrics(metrics, costEndpointId, request)
Decorators :
@Post('/endpoints/:id/metrics')
@UseInterceptors(new ProtobufInterceptor())
Parameters :
Name Optional
metrics No
costEndpointId No
request No
Returns : unknown
Async registerCostEndpoint
registerCostEndpoint(registerCostInput: RegisterCostEndpointDto, request)
Decorators :
@Post('/endpoints/')

Register cost endpoints in Turo

Parameters :
Name Type Optional
registerCostInput RegisterCostEndpointDto No
request No
Returns : unknown

registered cost endpoint or throw error

testConnection
testConnection(testEndpointDto: TestEndpointDto)
Decorators :
@Post('/endpoints/test')

Test connection based on input URL and credentials

Parameters :
Name Type Optional
testEndpointDto TestEndpointDto No
Returns : any

test connection api response

Async updateCostEndpoint
updateCostEndpoint(id: number, updateCostInput: UpdateCostEndpointDto, request)
Decorators :
@Post('/endpoints/:id')

Update registered cost endpoint for a project

Parameters :
Name Type Optional
id number No
updateCostInput UpdateCostEndpointDto No
request No
Returns : unknown

updated cost endpoint or throw error

import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  ParseIntPipe,
  Post,
  Query,
  ValidationPipe,
  Request,
  UnauthorizedException,
  UseInterceptors,
  NotFoundException,
} from "@nestjs/common";
import { ApiBearerAuth } from "@nestjs/swagger";
import { QueryCostDto } from "./dto/query-cost.dto";
import { RegisterCostEndpointDto } from "./dto/register-cost-endpoint.dto";
import { TestEndpointDto } from "./dto/test-endpoint.dto";
import { CostService } from "./cost.service";
import { PolicyService } from "../iam";
import {
  COST_CREATE_ACTION,
  COST_DELETE_ACTION,
  COST_PUSH_METRICS_ACTION,
  COST_READ_ACTION,
  COST_RESOURCE_NAME,
  COST_UPDATE_ACTION,
} from "./cost.constants";
import { FetchCostTagsDto } from "./dto/fetch-cost-tag.dto";
import { FetchResourcesDto } from "./dto/fetch-resource.dto";
import { UpdateCostEndpointDto } from "./dto/update-cost-endpoint.dto";
import { OpentelemetryProvider } from "../providers/opentelemetry/opentelemetry.provider";
import { ProtobufInterceptor } from "../common/interceptors/protobuf.interceptor";

/**
 * Controller responsible for handling requests related to turo cost operations.
 */
@ApiBearerAuth("access-token")
@Controller("cost")
export class CostController {
  constructor(
    private readonly costService: CostService,
    private readonly policyService: PolicyService,
  ) {}

  /**
   * Register cost endpoints in Turo
   * @param registerCostInput
   * @param request
   * @returns registered cost endpoint or throw error
   */
  @Post("/endpoints/")
  async registerCostEndpoint(
    @Body(ValidationPipe) registerCostInput: RegisterCostEndpointDto,
    @Request() request,
  ) {
    if (
      !(await this.policyService.checkPermission(
        request.user.permission,
        registerCostInput.projectID,
        COST_RESOURCE_NAME,
        COST_CREATE_ACTION,
      ))
    ) {
      throw new UnauthorizedException();
    }
    return this.costService.registerCostEndpoint({
      ...registerCostInput,
      config: registerCostInput.config as any,
    });
  }

  /**
   * Check if user token in request
   * has permission to register an cost
   * @param request
   * @param projectId
   * @returns boolean
   */
  @Get("/:projectId/endpoints/hasCreatePermission")
  canRegisterCostEndpoints(
    @Request() request,
    @Param("projectId") projectId: string,
  ) {
    return this.policyService.checkPermission(
      request.user.permission,
      projectId,
      COST_RESOURCE_NAME,
      COST_CREATE_ACTION,
    );
  }

  @Get("/providers")
  listProviders() {
    return this.costService.getCostProviderCategories();
  }

  @Get("/providers/:providerName/llm/models")
  listModels(@Param("providerName") providerName: string) {
    return this.costService.listModels(providerName);
  }

  @Get("/providers/:providerName/llm/getConfiguredCosts")
  getConfiguredCosts(@Param("providerName") providerName: string) {
    return this.costService.getConfiguredCosts(providerName);
  }

  /**
   * Test connection based on input URL and
   * credentials
   * @param testEndpointDto
   * @returns test connection api response
   */
  @Post("/endpoints/test")
  testConnection(@Body(ValidationPipe) testEndpointDto: TestEndpointDto) {
    return this.costService.testConnection(
      testEndpointDto.config,
      testEndpointDto.provider,
    );
  }

  @Post("endpoints/getTagKeys")
  getTagKeys(@Body(ValidationPipe) fetchCostTagsDto: FetchCostTagsDto) {
    return this.costService.getTagKeys(
      fetchCostTagsDto.config,
      fetchCostTagsDto.provider,
      fetchCostTagsDto.getTagsInput,
    );
  }

  @Post("endpoints/getTagValuesForKey")
  getTagValuesForKey(@Body(ValidationPipe) fetchCostTagsDto: FetchCostTagsDto) {
    return this.costService.getTagValuesForKey(
      fetchCostTagsDto.config,
      fetchCostTagsDto.provider,
      fetchCostTagsDto.getTagsInput,
    );
  }

  @Post("endpoints/:id/getTagKeys")
  async getTagKeysForConnection(@Param("id", new ParseIntPipe()) id: number) {
    const endpoint = await this.costService.getCostEndpointDetails(id);
    return this.costService.getTagKeys(
      endpoint.config,
      endpoint.endpointType,
      {},
    );
  }

  @Post("endpoints/:id/getTagValuesForKey/:tag")
  async getTagValuesForKeyForConnection(
    @Param("id", new ParseIntPipe()) id: number,
    @Param("tag") tag: string,
  ) {
    const endpoint = await this.costService.getCostEndpointDetails(id);
    return this.costService.getTagValuesForKey(
      endpoint.config,
      endpoint.endpointType,
      {
        Key: decodeURIComponent(tag),
      },
    );
  }

  /**
   * Get tags from connection based on input URL and
   * credentials
   * @param fetchCostTagsDto
   * @returns Get tags api response
   */
  @Post("/endpoints/getTagsData")
  getTagsData(@Body(ValidationPipe) fetchCostTagsDto: FetchCostTagsDto) {
    return this.costService.getTagsData(
      fetchCostTagsDto.config,
      fetchCostTagsDto.provider,
      fetchCostTagsDto.getTagsInput,
    );
  }

  /**
   * Get resources based on tagList filter
   * @param fetchResourceDto
   * @returns Get tags api response
   */
  @Post("/endpoints/getResourcesByTags")
  getResourcesByTags(
    @Body(ValidationPipe) fetchResourceDto: FetchResourcesDto,
  ) {
    return this.costService.getResourcesByTags(
      fetchResourceDto.config,
      fetchResourceDto.provider,
      fetchResourceDto.tagList,
    );
  }

  /**
   * Get tags from connection based on input connection id
   * @param id connection id
   * @returns Get tags api response
   */
  @Get("/endpoints/:id/getTags")
  async getTagsForConnection(@Param("id", new ParseIntPipe()) id: number) {
    const endpoint = await this.costService.getCostEndpointDetails(id);
    const today = new Date();
    const dayBeforeYesterdate = new Date(today);
    dayBeforeYesterdate.setDate(today.getDate() - 2);

    return this.costService.getTagsData(
      endpoint.config,
      endpoint.endpointType,
      {
        TimePeriod: {
          End: today.toISOString().split("T")[0], // Today's date in "YYYY-MM-DD" format
          Start: dayBeforeYesterdate.toISOString().split("T")[0], // Yesterday's date in "YYYY-MM-DD" format
        },
      },
    );
  }

  /**
   * Get resources from connection based on input connection id and
   * tag.
   * API can only filter one tag at a time due to aws provider limitation
   * @param id   connection id
   * @param tag  tag key value pair eg: key:Value1
   * @returns Get resource by tags api response
   */
  @Get("/endpoints/:id/getResourcesForTags/:tag")
  async getResourcesWithTagsForConnection(
    @Param("id", new ParseIntPipe()) id: number,
    @Param("tag") tag: string,
  ) {
    const endpoint = await this.costService.getCostEndpointDetails(id);
    return this.costService.getResourcesByTags(
      endpoint.config,
      endpoint.endpointType,
      [tag],
    );
  }

  @Post("/endpoints/:id/metrics")
  @UseInterceptors(
    new ProtobufInterceptor(
      OpentelemetryProvider.METRICS_PROTO_PATH,
      OpentelemetryProvider.METRICS_PROTO_MESSAGE_NAME,
    ),
  )
  async pushCostMetrics(
    @Body() metrics,
    @Param("id") costEndpointId,
    @Request() request,
  ) {
    const costEndpoint =
      await this.costService.getCostEndpointDetails(+costEndpointId);
    if (!costEndpoint) {
      throw new NotFoundException("cost endpoint with ID not found");
    }

    if (
      !(await this.policyService.checkPermission(
        request.user.permission,
        costEndpoint.projectID,
        COST_RESOURCE_NAME,
        COST_PUSH_METRICS_ACTION,
      ))
    ) {
      throw new UnauthorizedException();
    }

    console.log("Metrics received for cost endpoint: ", costEndpointId);
    console.log("Request headers: ", request.headers);
    console.log(JSON.stringify(metrics, null, 2));

    return this.costService.pushCostMetrics(metrics, costEndpoint);
  }

  /**
   * Get endpoint details by endpoint id without credentials
   * @param id
   * @returns cost endpoint
   */
  @Get("/endpoints/:id")
  async getCostEndpointDetails(@Param("id", new ParseIntPipe()) id: number) {
    const endpoint = await this.costService.getCostEndpointDetails(id);
    const provider = this.costService.getCostProvider(endpoint.endpointType);
    endpoint.config = provider.maskConfigSecret(endpoint.config);
    return endpoint;
  }

  /**
   * Update registered cost endpoint for
   * a project
   * @param id
   * @param updateCostInput
   * @param request
   * @returns updated cost endpoint or throw error
   */
  @Post("/endpoints/:id")
  async updateCostEndpoint(
    @Param("id", new ParseIntPipe()) id: number,
    @Body(ValidationPipe) updateCostInput: UpdateCostEndpointDto,
    @Request() request,
  ) {
    if (
      !(await this.policyService.checkPermission(
        request.user.permission,
        id,
        COST_RESOURCE_NAME,
        COST_UPDATE_ACTION,
      ))
    ) {
      throw new UnauthorizedException();
    }
    return this.costService.updateCostEndpoint(id, {
      ...updateCostInput,
      config: updateCostInput.config as any,
    });
  }

  /**
   * Delete registered cost endpoint for a project
   * @param id
   * @param request
   * @returns deleted endpoint or throws error
   */
  @Delete("/endpoints/:id")
  async deleteCostEndpoint(
    @Param("id", new ParseIntPipe()) id: number,
    @Request() request,
  ) {
    const costEndpoint = await this.costService.getCostEndpointDetails(id);
    if (
      !(await this.policyService.checkPermission(
        request.user.permission,
        costEndpoint.projectID,
        COST_RESOURCE_NAME,
        COST_DELETE_ACTION,
      ))
    ) {
      throw new UnauthorizedException();
    }
    return this.costService.deleteCostEndpoint(id);
  }

  /**
   * List projectwise cost incurred till date
   * will only return cost for project user has access to
   * @param request
   */
  @Get("/overview")
  async getCostOverview(@Request() request) {
    const costs = await this.costService.getCostOverview();
    return costs.filter((cost) => {
      return this.policyService.checkPermission(
        request.user.permission,
        cost.projectId,
        COST_RESOURCE_NAME,
        COST_READ_ACTION,
      );
    });
  }

  /**
   * Function to get cost data based on
   * input query params
   * @param projectID
   * @param query
   * @param request
   * @returns cost data for window
   */
  @Get("/:projectID")
  async getCost(
    @Param("projectID", new ParseIntPipe()) projectID: number,
    @Query() query: QueryCostDto,
    @Request() request,
  ) {
    if (
      !(await this.policyService.checkPermission(
        request.user.permission,
        projectID,
        COST_RESOURCE_NAME,
        COST_READ_ACTION,
      ))
    ) {
      throw new UnauthorizedException();
    }
    return this.costService.getCost(projectID, query.window);
  }

  /**
   * List registered cost endpoints for
   * input project ID
   * @param projectID
   * @param request
   * @returns List of cost endpoints with permissions on update/delete
   */
  @Get("/:projectID/endpoints")
  async getCostEndpoints(
    @Param("projectID", new ParseIntPipe()) projectID: string,
    @Request() request,
  ) {
    if (
      !(await this.policyService.checkPermission(
        request.user.permission,
        projectID,
        COST_RESOURCE_NAME,
        COST_READ_ACTION,
      ))
    ) {
      throw new UnauthorizedException();
    }
    const costEndpoints = await this.costService.getCostEndpoint(projectID);

    return costEndpoints.map((costEndpoint) => {
      const [canUpdate, canDelete] = [
        this.policyService.checkPermission(
          request.user.permission,
          costEndpoint.projectID,
          COST_RESOURCE_NAME,
          COST_UPDATE_ACTION,
        ),
        this.policyService.checkPermission(
          request.user.permission,
          costEndpoint.projectID,
          COST_RESOURCE_NAME,
          COST_DELETE_ACTION,
        ),
      ];

      return { ...costEndpoint, update: canUpdate, delete: canDelete };
    });
  }
}

results matching ""

    No results matching ""