src/services/assets.service.tsmodifiedimport { LoggerFactory } from "../util/logger";
import { query } from "../db/db";
import { NotFoundError } from "../types/errortypes";
import { getLoggedInUsername } from "../util/username";
import { PagingQueryParam } from "../models/ingress";
import { IndexedItem } from "../util/pagination.util";
const logger = LoggerFactory("AssetsService");
const SQL = {
LIST: `SELECT * FROM xyz."fn_GetAssetList"($1, $2, $3)`,
GET_BY_ID: `SELECT * FROM xyz."fn_GetAsset"($1, $2)`,
INSERT: `SELECT * FROM xyz."fn_InsertAsset"($1, $2, $3, $4)`,
UPDATE: `SELECT * FROM xyz."fn_UpdateAsset"($1, $2, $3, $4, $5, $6)`,
};
// Partial-update payload for PATCH. Only the fields present are updated; absent
// fields are passed as null so the DB function leaves the existing column value
// unchanged. Per PAPI-3631 scope only `name` is patchable; relational fields
// (AssetTypeId, ParentAssetId) are out of scope.
export interface AssetPatch {
name?: string;
}
export interface Asset {
assetId: string;
name: string;
assetTypeId: string;
createdBy: string;
insertedOn: Date;
lastModifiedOn: Date | null;
lastModifiedBy: string | null;
}
function mapRow(row: any): Asset {
return {
assetId: row.AssetId,
name: row.Name,
assetTypeId: row.AssetTypeId,
createdBy: row.CreatedBy,
insertedOn: row.InsertedOn,
lastModifiedOn: row.LastModifiedOn ?? null,
lastModifiedBy: row.LastModifiedBy ?? null,
};
}
function mapError(err: unknown): never {
if (err instanceof Error && err.message.includes('AssetType with id')) {
throw new NotFoundError(err.message);
}
throw err;
}
export async function listAssets(projectId: string, paging: PagingQueryParam): Promise<(Asset & IndexedItem)[]> {
try {
const { rows } = await query(SQL.LIST, [projectId, paging.lastFetchedIndexId, paging.size]);
return rows.map((row: any) => ({ ...mapRow(row), indexId: row.Id }));
} catch (err) {
logger.error("Error while listing assets", err);
throw err;
}
}
export async function getAssetById(projectId: string, assetId: string): Promise<Asset> {
try {
const { rows } = await query(SQL.GET_BY_ID, [projectId, assetId]);
if (rows.length === 0) {
throw new NotFoundError(`Asset with id: ${assetId} not found.`);
}
return mapRow(rows[0]);
} catch (err) {
logger.error("Error while fetching asset", err);
throw err;
}
}
export async function createAsset(projectId: string, assetTypeId: string, name: string): Promise<Asset> {
try {
const { rows } = await query(SQL.INSERT, [projectId, assetTypeId, name, getLoggedInUsername()]);
return mapRow(rows[0]);
} catch (err) {
logger.error("Error while creating asset", err);
mapError(err);
}
}
export async function updateAsset(projectId: string, assetId: string, patch: AssetPatch): Promise<Asset> {
try {
const { rows } = await query(SQL.UPDATE, [
projectId,
assetId,
patch.name ?? null,
null, // assetTypeId — not patchable via this endpoint (out of PAPI-3631 scope)
null, // parentAssetId — not patchable via this endpoint (out of PAPI-3631 scope)
getLoggedInUsername(),
]);
if (rows.length === 0) {
throw new NotFoundError(`Asset with id: ${assetId} not found.`);
}
return mapRow(rows[0]);
} catch (err) {
logger.error("Error while updating asset", err);
mapError(err);
}
}