Fix review findings for info broker removal service FRE-5402
P0 fixes: - Add CANCELLED status to RemovalStatus enum (types + Prisma schema) - Use CANCELLED instead of REJECTED for user-initiated cancellations - Add null guard for req.broker?.name in GET /request/:id - Remove unsafe 'as any' casts in RemoveBrokersService.ts - Add type-safe toPersonalInfo() validator for JSON deserialization - Type RemovalRequestWithBroker properly in getRemovalStatus() - Fix alert: any to NormalizedAlertInput in BrokerAlertPipeline P1 fixes: - Fix admin role check: remove non-existent 'admin', only check 'support' - Fix BrokerDefinition.category type from string to BrokerCategory - Add complete OpenAPI spec for all removebrokers routes and schemas
This commit is contained in:
3629
packages/api/src/openapi/spec.json
Normal file
3629
packages/api/src/openapi/spec.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -270,7 +270,7 @@ export async function removebrokersRoutes(fastify: FastifyInstance) {
|
||||
request: {
|
||||
id: req.id,
|
||||
brokerId: req.brokerId,
|
||||
brokerName: req.broker.name,
|
||||
brokerName: req.broker?.name || null,
|
||||
status: req.status,
|
||||
method: req.method,
|
||||
attempts: req.attempts,
|
||||
@@ -313,13 +313,13 @@ export async function removebrokersRoutes(fastify: FastifyInstance) {
|
||||
|
||||
await prisma.removalRequest.update({
|
||||
where: { id },
|
||||
data: { status: RemovalStatus.REJECTED },
|
||||
data: { status: RemovalStatus.CANCELLED },
|
||||
});
|
||||
|
||||
return reply.send({
|
||||
request: {
|
||||
id: req.id,
|
||||
status: RemovalStatus.REJECTED,
|
||||
status: RemovalStatus.CANCELLED,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -335,7 +335,7 @@ export async function removebrokersRoutes(fastify: FastifyInstance) {
|
||||
return reply.code(401).send({ error: 'User not authenticated' });
|
||||
}
|
||||
|
||||
if (authReq.user.role !== 'admin' && authReq.user.role !== 'support') {
|
||||
if (authReq.user.role !== 'support') {
|
||||
return reply.code(403).send({ error: 'Admin access required' });
|
||||
}
|
||||
|
||||
|
||||
@@ -838,6 +838,7 @@ enum RemovalStatus {
|
||||
COMPLETED
|
||||
FAILED
|
||||
REJECTED
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
model InfoBroker {
|
||||
|
||||
@@ -399,6 +399,7 @@ export const RemovalStatus = {
|
||||
COMPLETED: "COMPLETED",
|
||||
FAILED: "FAILED",
|
||||
REJECTED: "REJECTED",
|
||||
CANCELLED: "CANCELLED",
|
||||
} as const;
|
||||
export type RemovalStatus = (typeof RemovalStatus)[keyof typeof RemovalStatus];
|
||||
|
||||
@@ -416,7 +417,7 @@ export interface BrokerDefinition {
|
||||
id: string;
|
||||
name: string;
|
||||
domain: string;
|
||||
category: string;
|
||||
category: BrokerCategory;
|
||||
removalMethod: RemovalMethod;
|
||||
removalUrl?: string;
|
||||
requiresAccount: boolean;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AlertSource, AlertCategory, Severity, EntityType } from "@shieldai/types";
|
||||
import { AlertSource, AlertCategory, Severity, EntityType, NormalizedAlertInput } from "@shieldai/types";
|
||||
|
||||
export interface BrokerAlertInput {
|
||||
userId: string;
|
||||
@@ -55,7 +55,7 @@ export class BrokerAlertPipeline {
|
||||
return this.normalizeAndSend(alert);
|
||||
}
|
||||
|
||||
private async normalizeAndSend(alert: any) {
|
||||
private async normalizeAndSend(alert: NormalizedAlertInput) {
|
||||
try {
|
||||
const { correlationPipeline } = await import("@shieldai/correlation");
|
||||
return correlationPipeline.normalizeAlert(alert);
|
||||
|
||||
@@ -3,6 +3,34 @@ import { RemovalStatus, RemovalMethod } from "@shieldai/types";
|
||||
import { getBrokerById, getActiveBrokers } from "./brokerRegistry";
|
||||
import type { PersonalInfo, RemovalJob, BrokerEntry } from "./types";
|
||||
import { MAX_REMOVAL_ATTEMPTS, RETRY_DELAY_MS } from "./types";
|
||||
import type { RemovalRequest as PrismaRemovalRequest, InfoBroker } from "@shieldai/db";
|
||||
|
||||
function toPersonalInfo(raw: unknown): PersonalInfo | null {
|
||||
if (typeof raw !== "object" || raw === null) return null;
|
||||
const obj = raw as Record<string, unknown>;
|
||||
if (typeof obj.fullName !== "string") return null;
|
||||
let address: PersonalInfo["address"] = undefined;
|
||||
if (typeof obj.address === "object" && obj.address !== null) {
|
||||
const addr = obj.address as Record<string, unknown>;
|
||||
address = {
|
||||
street: typeof addr.street === "string" ? addr.street : undefined,
|
||||
city: typeof addr.city === "string" ? addr.city : undefined,
|
||||
state: typeof addr.state === "string" ? addr.state : undefined,
|
||||
zip: typeof addr.zip === "string" ? addr.zip : undefined,
|
||||
};
|
||||
}
|
||||
return {
|
||||
fullName: obj.fullName,
|
||||
email: typeof obj.email === "string" ? obj.email : undefined,
|
||||
phone: typeof obj.phone === "string" ? obj.phone : undefined,
|
||||
address,
|
||||
dob: typeof obj.dob === "string" ? obj.dob : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
type RemovalRequestWithBroker = PrismaRemovalRequest & {
|
||||
broker: InfoBroker;
|
||||
};
|
||||
|
||||
export class RemoveBrokersService {
|
||||
async scanForListings(subscriptionId: string, personalInfo: PersonalInfo) {
|
||||
@@ -88,7 +116,7 @@ export class RemoveBrokersService {
|
||||
subscriptionId,
|
||||
brokerId,
|
||||
status: RemovalStatus.PENDING,
|
||||
personalInfo: personalInfo as any,
|
||||
personalInfo: JSON.parse(JSON.stringify(personalInfo)),
|
||||
method: broker.removalMethod,
|
||||
notes,
|
||||
},
|
||||
@@ -139,7 +167,7 @@ export class RemoveBrokersService {
|
||||
requestId: request.id,
|
||||
brokerId: request.brokerId,
|
||||
brokerName: getBrokerById(request.brokerId)?.name || request.brokerId,
|
||||
personalInfo: request.personalInfo as PersonalInfo,
|
||||
personalInfo: toPersonalInfo(request.personalInfo)!,
|
||||
method: request.method,
|
||||
attempt: request.attempts + 1,
|
||||
};
|
||||
@@ -203,7 +231,10 @@ export class RemoveBrokersService {
|
||||
throw new Error(`Removal request not found: ${requestId}`);
|
||||
}
|
||||
|
||||
const personalInfo = request.personalInfo as PersonalInfo;
|
||||
const personalInfo = toPersonalInfo(request.personalInfo);
|
||||
if (!personalInfo) {
|
||||
throw new Error(`Invalid personal info in request ${requestId}`);
|
||||
}
|
||||
const stillListed = await this.checkBrokerListing(
|
||||
request.broker,
|
||||
personalInfo,
|
||||
@@ -242,7 +273,7 @@ export class RemoveBrokersService {
|
||||
orderBy: { updatedAt: "desc" },
|
||||
});
|
||||
|
||||
return requests.map((r: any) => ({
|
||||
return requests.map((r: RemovalRequestWithBroker) => ({
|
||||
id: r.id,
|
||||
brokerId: r.brokerId,
|
||||
brokerName: r.broker.name,
|
||||
|
||||
Reference in New Issue
Block a user