diff --git a/src/app/api/admin/companies/[id]/route.ts b/src/app/api/admin/companies/[id]/route.ts index 6c1c20f..b4da361 100644 --- a/src/app/api/admin/companies/[id]/route.ts +++ b/src/app/api/admin/companies/[id]/route.ts @@ -87,13 +87,13 @@ export async function PATCH( const mergedInput = mergePayload(baseForm, rawBody) const form = sanitizeCompanyInput(mergedInput, existing.tenantId) - const createData = buildCompanyData(form, existing.tenantId) + const createData = buildCompanyData(form, existing.tenantId) as any const { tenantId: _omitTenant, ...updateData } = createData void _omitTenant const company = await prisma.company.update({ where: { id }, - data: updateData, + data: updateData as any, }) if (company.provisioningCode) { diff --git a/src/app/api/admin/companies/route.ts b/src/app/api/admin/companies/route.ts index 7839c5a..18df5f7 100644 --- a/src/app/api/admin/companies/route.ts +++ b/src/app/api/admin/companies/route.ts @@ -43,9 +43,9 @@ export async function POST(request: Request) { const company = await prisma.company.create({ data: { - ...buildCompanyData(form, tenantId), + ...(buildCompanyData(form, tenantId) as any), provisioningCode, - }, + } as any, }) if (company.provisioningCode) { diff --git a/src/components/admin/machines/admin-machine-details.client.tsx b/src/components/admin/machines/admin-machine-details.client.tsx index de6bb86..e466c50 100644 --- a/src/components/admin/machines/admin-machine-details.client.tsx +++ b/src/components/admin/machines/admin-machine-details.client.tsx @@ -13,7 +13,10 @@ import { Skeleton } from "@/components/ui/skeleton" import type { Id } from "@/convex/_generated/dataModel" export function AdminMachineDetailsClient({ tenantId, machineId }: { tenantId: string; machineId: string }) { - const single = useQuery((api as any).machines.getById, { id: machineId as Id<"machines">, includeMetadata: true }) as + const single = useQuery( + (api as any).machines.getById, + machineId ? ({ id: machineId as Id<"machines">, includeMetadata: true } as const) : ("skip" as const) + ) as | Record | null | undefined diff --git a/src/components/admin/machines/machine-breadcrumbs.client.tsx b/src/components/admin/machines/machine-breadcrumbs.client.tsx index 9736d02..1f19f4c 100644 --- a/src/components/admin/machines/machine-breadcrumbs.client.tsx +++ b/src/components/admin/machines/machine-breadcrumbs.client.tsx @@ -9,7 +9,10 @@ import { useAuth } from "@/lib/auth-client" export function MachineBreadcrumbs({ tenantId, machineId }: { tenantId: string; machineId: string }) { const { convexUserId } = useAuth() - const item = useQuery((api as any).machines.getById, { id: machineId as Id<"machines">, includeMetadata: false }) as + const item = useQuery( + (api as any).machines.getById, + machineId ? ({ id: machineId as Id<"machines">, includeMetadata: false } as const) : ("skip" as const) + ) as | { hostname: string } | null | undefined diff --git a/src/server/company-service.ts b/src/server/company-service.ts index 57f35dc..0d1980f 100644 --- a/src/server/company-service.ts +++ b/src/server/company-service.ts @@ -1,4 +1,4 @@ -import { Prisma, type Company, type CompanyStateRegistrationType } from "@prisma/client" +import { Prisma, type Company } from "@prisma/client" import { prisma } from "@/lib/prisma" import { ZodError } from "zod" @@ -17,6 +17,13 @@ export type NormalizedCompany = CompanyFormValues & { updatedAt: string } +// Local representation of the DB enum to avoid relying on Prisma enum exports +type DbCompanyStateRegistrationType = "STANDARD" | "EXEMPT" | "SIMPLES" + +function asDbStateRegistrationType(value: unknown): DbCompanyStateRegistrationType | undefined { + return value === "STANDARD" || value === "EXEMPT" || value === "SIMPLES" ? value : undefined +} + function slugify(value?: string | null): string { if (!value) return "" const ascii = value @@ -47,7 +54,7 @@ function ensureSlugValue( const STATE_REGISTRATION_TYPE_TO_PRISMA: Record< CompanyStateRegistrationTypeOption, - CompanyStateRegistrationType + DbCompanyStateRegistrationType > = { standard: "STANDARD", exempt: "EXEMPT", @@ -55,7 +62,7 @@ const STATE_REGISTRATION_TYPE_TO_PRISMA: Record< } const STATE_REGISTRATION_TYPE_FROM_PRISMA: Record< - CompanyStateRegistrationType, + DbCompanyStateRegistrationType, CompanyStateRegistrationTypeOption > = { STANDARD: "standard", @@ -206,7 +213,7 @@ export function sanitizeCompanyInput(input: unknown, tenantId: string): CompanyF export function buildCompanyData( payload: CompanyFormValues, tenantId: string -): Omit { +): Record { const stateRegistrationType = payload.stateRegistrationType ? STATE_REGISTRATION_TYPE_TO_PRISMA[payload.stateRegistrationType as CompanyStateRegistrationTypeOption] : null @@ -262,7 +269,7 @@ export function buildCompanyData( } } -export function normalizeCompany(company: Company): NormalizedCompany { +export function normalizeCompany(company: any): NormalizedCompany { const communicationChannels = normalizeChannels( company.communicationChannels as CompanyCommunicationChannels | null | undefined ) @@ -276,9 +283,10 @@ export function normalizeCompany(company: Company): NormalizedCompany { tradeName: company.tradeName, cnpj: company.cnpj, stateRegistration: company.stateRegistration, - stateRegistrationType: company.stateRegistrationType - ? STATE_REGISTRATION_TYPE_FROM_PRISMA[company.stateRegistrationType] - : undefined, + stateRegistrationType: (() => { + const t = asDbStateRegistrationType(company.stateRegistrationType) + return t ? STATE_REGISTRATION_TYPE_FROM_PRISMA[t] : undefined + })(), primaryCnae: company.primaryCnae, description: company.description, domain: company.domain, @@ -379,7 +387,7 @@ function parseJsonValue(value: string | null): Prisma.JsonValue | null { } } -function mapRawRowToCompany(row: RawCompanyRow): Company { +function mapRawRowToCompany(row: RawCompanyRow): any { return { id: row.id, tenantId: row.tenantId, @@ -397,7 +405,7 @@ function mapRawRowToCompany(row: RawCompanyRow): Company { tradeName: row.tradeName, stateRegistration: row.stateRegistration, stateRegistrationType: row.stateRegistrationType - ? (row.stateRegistrationType as CompanyStateRegistrationType) + ? (row.stateRegistrationType as DbCompanyStateRegistrationType) : null, primaryCnae: row.primaryCnae, timezone: row.timezone, @@ -469,7 +477,7 @@ const COMPANY_BASE_SELECT = Prisma.sql` FROM "Company" ` -export async function fetchCompaniesByTenant(tenantId: string): Promise { +export async function fetchCompaniesByTenant(tenantId: string): Promise { const rows = await prisma.$queryRaw(Prisma.sql` ${COMPANY_BASE_SELECT} WHERE tenantId = ${tenantId} @@ -478,7 +486,7 @@ export async function fetchCompaniesByTenant(tenantId: string): Promise { +export async function fetchCompanyById(id: string): Promise { const rows = await prisma.$queryRaw(Prisma.sql` ${COMPANY_BASE_SELECT} WHERE id = ${id} diff --git a/tests/machines.getById.test.ts b/tests/machines.getById.test.ts new file mode 100644 index 0000000..08bfeeb --- /dev/null +++ b/tests/machines.getById.test.ts @@ -0,0 +1,78 @@ +import { describe, it, expect, vi } from "vitest" + +import type { Doc, Id } from "../convex/_generated/dataModel" +import { getById } from "../convex/machines" + +const FIXED_NOW = 1_706_071_200_000 + +function buildMachine(overrides: Partial> = {}): Doc<"machines"> { + const machine: Record = { + _id: "machine_1" as Id<"machines">, + tenantId: "tenant-1", + companyId: undefined, + companySlug: undefined, + authUserId: undefined, + authEmail: undefined, + persona: undefined, + assignedUserId: undefined, + assignedUserEmail: undefined, + assignedUserName: undefined, + assignedUserRole: undefined, + hostname: "desktop-01", + osName: "Windows", + osVersion: "11", + architecture: "x86_64", + macAddresses: ["001122334455"], + serialNumbers: ["SN123"], + fingerprint: "fingerprint", + metadata: { + metrics: { cpu: { usage: 12 }, memory: { total: 8 * 1024 ** 3 } }, + inventory: { cpu: { model: "Intel" }, os: { name: "Windows" } }, + postureAlerts: [{ kind: "CPU_HIGH", severity: "warning" }], + lastPostureAt: FIXED_NOW - 5000, + }, + lastHeartbeatAt: FIXED_NOW - 1000, + status: undefined, + isActive: true, + createdAt: FIXED_NOW - 10_000, + updatedAt: FIXED_NOW - 5_000, + registeredBy: "agent:desktop", + linkedUserIds: [], + remoteAccess: null, + } + return { ...(machine as Doc<"machines">), ...overrides } +} + +describe("convex.machines.getById", () => { + it("returns machine details including metadata when includeMetadata=true", async () => { + vi.useFakeTimers() + vi.setSystemTime(FIXED_NOW) + + const machine = buildMachine() + + const db = { + get: vi.fn(async (id: Id<"machines">) => { + if (id === machine._id) return machine + return null + }), + query: vi.fn((_table: string) => ({ + withIndex: vi.fn((_name: string, _cb: unknown) => ({ + collect: vi.fn(async () => [ + { revoked: false, expiresAt: FIXED_NOW + 60_000, lastUsedAt: FIXED_NOW - 1000, usageCount: 5 }, + ]), + })), + collect: vi.fn(async () => []), + })), + } + + const ctx = { db } as unknown as Parameters[0] + const result = await getById(ctx, { id: machine._id, includeMetadata: true }) + + expect(result).toBeTruthy() + expect(result?.metrics).toBeTruthy() + expect(result?.inventory).toBeTruthy() + expect(result?.postureAlerts?.length).toBeGreaterThan(0) + expect(result?.token).toBeTruthy() + }) +}) +