/** * GraphQL `aggregatedPartCounts` argument builder (inline `args: { ... }` fragment). * `machineRef`, `metricKey`, `windowStartAt`, and `windowEndAt` are required in `args`. * Optional tuning: `bucketWidth`, `bucketResolution`, and `windowWidth` are passed through when set (see MM GraphQL schema). * * @see https://developers.machinemetrics.com/docs/graphql/api */ import type { MMAggregatedPartCountBucket } from "./types/mm-response-interfaces.js"; import { escapeGraphqlStringLiteral } from "./graphql-string-literal.js"; export const MM_DEFAULT_PART_COUNT_METRIC_KEY = "part-count" as const; export function buildAggregatedPartCountsArgsFragment(options: { machineRef: number; metricKey: string; windowStartAt: string; windowEndAt: string; bucketWidth?: string; bucketResolution?: number; windowWidth?: string; }): string { const refInt = Math.trunc(Number(options.machineRef)); if (!Number.isFinite(refInt)) { throw new RangeError("machineRef must be a finite number"); } const parts: string[] = [ `machineRef: ${refInt}`, `metricKey: "${escapeGraphqlStringLiteral(options.metricKey)}"`, `windowStartAt: "${escapeGraphqlStringLiteral(options.windowStartAt)}"`, `windowEndAt: "${escapeGraphqlStringLiteral(options.windowEndAt)}"`, ]; if (options.bucketWidth !== undefined) { parts.push(`bucketWidth: "${escapeGraphqlStringLiteral(options.bucketWidth)}"`); } if (options.bucketResolution !== undefined) { const bucketResolution = Number(options.bucketResolution); if (!Number.isFinite(bucketResolution)) { throw new RangeError("bucketResolution must be a finite number"); } parts.push(`bucketResolution: ${bucketResolution}`); } if (options.windowWidth !== undefined) { parts.push(`windowWidth: "${escapeGraphqlStringLiteral(options.windowWidth)}"`); } return parts.join(", "); } export function buildAggregatedPartCountsQuery(options: { machineRef: number; metricKey: string; windowStartAt: string; windowEndAt: string; bucketWidth?: string; bucketResolution?: number; windowWidth?: string; }): string { const args = buildAggregatedPartCountsArgsFragment(options); return `query aggregatedPartCountsForMachine { aggregatedPartCounts(args: { ${args} }) { machineRef metricKey sumOfValues eventCount bucketStartAt bucketEndAt bucketWidth firstValue lastValue } }`; } /** * Parses one `aggregatedPartCounts` bucket from JSON. * Per MM GraphQL, each returned row includes non-null strings for `metricKey`, `bucketStartAt`, `bucketEndAt`, and `bucketWidth` (width may be empty); absence, null, or wrong types throw. */ export function parseAggregatedPartCountBucketRow( raw: unknown ): MMAggregatedPartCountBucket { if (typeof raw !== "object" || raw === null) { throw new Error("aggregatedPartCounts: invalid row"); } const o = raw as Record; const machineRef = typeof o.machineRef === "number" ? o.machineRef : Number(o.machineRef); const sumOfValues = typeof o.sumOfValues === "number" ? o.sumOfValues : Number(o.sumOfValues); const eventCount = typeof o.eventCount === "number" ? o.eventCount : Number(o.eventCount); if (typeof o.metricKey !== "string" || o.metricKey.length === 0) { throw new Error("aggregatedPartCounts: invalid or missing metricKey"); } if (typeof o.bucketStartAt !== "string" || o.bucketStartAt.length === 0) { throw new Error("aggregatedPartCounts: invalid or missing bucketStartAt"); } if (typeof o.bucketEndAt !== "string" || o.bucketEndAt.length === 0) { throw new Error("aggregatedPartCounts: invalid or missing bucketEndAt"); } if (typeof o.bucketWidth !== "string") { throw new Error("aggregatedPartCounts: invalid or missing bucketWidth"); } const metricKey = o.metricKey; const bucketStartAt = o.bucketStartAt; const bucketEndAt = o.bucketEndAt; const bucketWidth = o.bucketWidth; if ( !Number.isFinite(machineRef) || !Number.isFinite(sumOfValues) || !Number.isFinite(eventCount) ) { throw new Error("aggregatedPartCounts: missing numeric fields"); } const firstValue = o.firstValue === null || o.firstValue === undefined ? null : typeof o.firstValue === "number" ? o.firstValue : Number(o.firstValue); const lastValue = o.lastValue === null || o.lastValue === undefined ? null : typeof o.lastValue === "number" ? o.lastValue : Number(o.lastValue); return { machineRef, metricKey, sumOfValues, eventCount, bucketStartAt, bucketEndAt, bucketWidth, firstValue: firstValue === null || Number.isFinite(firstValue) ? firstValue : null, lastValue: lastValue === null || Number.isFinite(lastValue) ? lastValue : null, }; }