@rawdash/connector-new-relic 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -46,6 +46,7 @@ type NewRelicCredentials = typeof newRelicCredentials;
|
|
|
46
46
|
declare const newRelicResources: {
|
|
47
47
|
readonly newrelic_alert_condition: {
|
|
48
48
|
readonly shape: "entity";
|
|
49
|
+
readonly filterable: [];
|
|
49
50
|
readonly description: "NRQL alert conditions with name, enabled state, policy id, type, and the underlying NRQL query string.";
|
|
50
51
|
readonly endpoint: "GraphQL query: actor.account.alerts.nrqlConditionsSearch { nrqlConditions { ... } }";
|
|
51
52
|
readonly responses: {
|
|
@@ -69,6 +70,7 @@ declare const newRelicResources: {
|
|
|
69
70
|
};
|
|
70
71
|
readonly newrelic_alert_violation: {
|
|
71
72
|
readonly shape: "event";
|
|
73
|
+
readonly filterable: [];
|
|
72
74
|
readonly description: "AI alert violation events. Each row from the NrAiIncident event type becomes one event with openedAt / closedAt and the underlying condition / policy metadata.";
|
|
73
75
|
readonly endpoint: "GraphQL nrql() against SELECT ... FROM NrAiIncident WHERE openedAt > ...";
|
|
74
76
|
readonly notes: "Append-only across syncs; the connector filters NrAiIncident by `openedAt` against `options.since` (or the configured lookback) to avoid re-emitting old incidents.";
|
|
@@ -120,6 +122,7 @@ declare class NewRelicConnector extends BaseConnector<NewRelicSettings, NewRelic
|
|
|
120
122
|
static readonly resources: {
|
|
121
123
|
readonly newrelic_alert_condition: {
|
|
122
124
|
readonly shape: "entity";
|
|
125
|
+
readonly filterable: [];
|
|
123
126
|
readonly description: "NRQL alert conditions with name, enabled state, policy id, type, and the underlying NRQL query string.";
|
|
124
127
|
readonly endpoint: "GraphQL query: actor.account.alerts.nrqlConditionsSearch { nrqlConditions { ... } }";
|
|
125
128
|
readonly responses: {
|
|
@@ -143,6 +146,7 @@ declare class NewRelicConnector extends BaseConnector<NewRelicSettings, NewRelic
|
|
|
143
146
|
};
|
|
144
147
|
readonly newrelic_alert_violation: {
|
|
145
148
|
readonly shape: "event";
|
|
149
|
+
readonly filterable: [];
|
|
146
150
|
readonly description: "AI alert violation events. Each row from the NrAiIncident event type becomes one event with openedAt / closedAt and the underlying condition / policy metadata.";
|
|
147
151
|
readonly endpoint: "GraphQL nrql() against SELECT ... FROM NrAiIncident WHERE openedAt > ...";
|
|
148
152
|
readonly notes: "Append-only across syncs; the connector filters NrAiIncident by `openedAt` against `options.since` (or the configured lookback) to avoid re-emitting old incidents.";
|
package/dist/index.js
CHANGED
|
@@ -212,12 +212,14 @@ var FACET_FIELDS = /* @__PURE__ */ new Set(["facet"]);
|
|
|
212
212
|
var newRelicResources = defineResources({
|
|
213
213
|
newrelic_alert_condition: {
|
|
214
214
|
shape: "entity",
|
|
215
|
+
filterable: [],
|
|
215
216
|
description: "NRQL alert conditions with name, enabled state, policy id, type, and the underlying NRQL query string.",
|
|
216
217
|
endpoint: "GraphQL query: actor.account.alerts.nrqlConditionsSearch { nrqlConditions { ... } }",
|
|
217
218
|
responses: { alert_conditions: nrqlConditionsSearchResultSchema }
|
|
218
219
|
},
|
|
219
220
|
newrelic_alert_violation: {
|
|
220
221
|
shape: "event",
|
|
222
|
+
filterable: [],
|
|
221
223
|
description: "AI alert violation events. Each row from the NrAiIncident event type becomes one event with openedAt / closedAt and the underlying condition / policy metadata.",
|
|
222
224
|
endpoint: "GraphQL nrql() against SELECT ... FROM NrAiIncident WHERE openedAt > ...",
|
|
223
225
|
notes: "Append-only across syncs; the connector filters NrAiIncident by `openedAt` against `options.since` (or the configured lookback) to avoid re-emitting old incidents.",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../connector-shared/src/errors.ts","../../../connector-shared/src/retry.ts","../../../connector-shared/src/version.ts","../../../connector-shared/src/request.ts","../../../connector-shared/src/rate-limit.ts","../../../connector-shared/src/map-concurrent.ts","../../../connector-shared/src/sanitize.ts","../../../connector-shared/src/epoch.ts","../../../connector-shared/src/pagination.ts","../../../connector-shared/src/logger.ts","../src/new-relic.ts","../src/index.ts"],"sourcesContent":["import type { HttpResponse } from './types';\n\nexport type HttpErrorKind =\n | 'transient'\n | 'rate_limit'\n | 'auth'\n | 'upstream_bug'\n | 'client_bug';\n\nexport abstract class HttpClientError extends Error {\n abstract readonly kind: HttpErrorKind;\n readonly response?: HttpResponse;\n\n constructor(message: string, response?: HttpResponse) {\n super(message);\n this.name = new.target.name;\n this.response = response;\n }\n}\n\nexport class TransientError extends HttpClientError {\n readonly kind = 'transient' as const;\n}\n\nexport class RateLimitError extends HttpClientError {\n readonly kind = 'rate_limit' as const;\n readonly retryAfter?: Date;\n\n constructor(message: string, response?: HttpResponse, retryAfter?: Date) {\n super(message, response);\n this.retryAfter = retryAfter;\n }\n}\n\nexport class AuthError extends HttpClientError {\n readonly kind = 'auth' as const;\n}\n\nexport class UpstreamBugError extends HttpClientError {\n readonly kind = 'upstream_bug' as const;\n}\n\nexport class ClientBugError extends HttpClientError {\n readonly kind = 'client_bug' as const;\n}\n\nexport function classifyStatus(status: number): HttpErrorKind {\n if (status === 429) {\n return 'rate_limit';\n }\n if (status === 401 || status === 403) {\n return 'auth';\n }\n if (status === 408) {\n return 'transient';\n }\n if (status >= 500) {\n return 'upstream_bug';\n }\n if (status >= 400) {\n return 'client_bug';\n }\n return 'client_bug';\n}\n\nexport function errorForStatus(\n message: string,\n response: HttpResponse,\n retryAfter?: Date,\n): HttpClientError {\n const kind = classifyStatus(response.status);\n switch (kind) {\n case 'rate_limit':\n return new RateLimitError(message, response, retryAfter);\n case 'auth':\n return new AuthError(message, response);\n case 'transient':\n return new TransientError(message, response);\n case 'upstream_bug':\n return new UpstreamBugError(message, response);\n case 'client_bug':\n return new ClientBugError(message, response);\n }\n}\n","import { HttpClientError, RateLimitError, TransientError } from './errors';\n\nexport interface RetryPolicy {\n maxAttempts?: number;\n initialDelayMs?: number;\n maxDelayMs?: number;\n retryOn?: (status: number | null, err?: Error) => boolean;\n}\n\nexport const defaultRetryOn = (status: number | null, err?: Error): boolean => {\n if (err instanceof RateLimitError) {\n return true;\n }\n if (err instanceof TransientError) {\n return true;\n }\n if (status === null) {\n return err instanceof Error && !(err instanceof HttpClientError);\n }\n if (status === 408 || status === 429) {\n return true;\n }\n if (status >= 500) {\n return true;\n }\n return false;\n};\n\nexport function backoffDelayMs(\n attempt: number,\n policy: Required<Pick<RetryPolicy, 'initialDelayMs' | 'maxDelayMs'>>,\n): number {\n const base = policy.initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, policy.maxDelayMs);\n}\n\nexport function parseRetryAfter(\n headerValue: string | null,\n now: Date = new Date(),\n): Date | undefined {\n if (!headerValue) {\n return undefined;\n }\n const trimmed = headerValue.trim();\n if (/^\\d+$/.test(trimmed)) {\n return new Date(now.getTime() + Number(trimmed) * 1000);\n }\n const parsed = Date.parse(trimmed);\n if (Number.isNaN(parsed)) {\n return undefined;\n }\n return new Date(parsed);\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n return Promise.reject(signal.reason ?? new Error('Aborted'));\n }\n return new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal!.reason ?? new Error('Aborted'));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n","export const HTTP_CLIENT_VERSION = '0.0.0';\n\nexport const DEFAULT_USER_AGENT = `rawdash-connector/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n\nexport function connectorUserAgent(connectorId: string): string {\n return `rawdash-connector-${connectorId}/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n}\n","import {\n AuthError,\n ClientBugError,\n HttpClientError,\n RateLimitError,\n TransientError,\n UpstreamBugError,\n errorForStatus,\n} from './errors';\nimport { defaultRetryOn, parseRetryAfter, sleep } from './retry';\nimport type { FetchLike, HttpMethod, HttpRequest, HttpResponse } from './types';\nimport { DEFAULT_USER_AGENT } from './version';\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_ATTEMPTS = 3;\nconst DEFAULT_INITIAL_DELAY_MS = 1000;\nconst DEFAULT_MAX_DELAY_MS = 60_000;\nconst OBSERVER_TIMEOUT_MS = 250;\n\nexport interface RequestObservation {\n url: string;\n method: HttpMethod;\n status: number;\n resource: string;\n requestId: string;\n body: unknown;\n}\n\nexport type RequestObserver = (\n event: RequestObservation,\n) => void | Promise<void>;\n\nexport interface RequestOptions {\n fetch?: FetchLike;\n observer?: RequestObserver;\n resource: string;\n requestId?: string;\n}\n\nasync function notifyObserver(\n observer: RequestObserver,\n event: RequestObservation,\n): Promise<void> {\n let result: void | Promise<void>;\n try {\n result = observer(event);\n } catch (err) {\n console.warn('[connector-shared] request observer threw:', err);\n return;\n }\n if (!(result instanceof Promise)) {\n return;\n }\n const guarded = result.catch((err) => {\n console.warn('[connector-shared] request observer rejected:', err);\n });\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, OBSERVER_TIMEOUT_MS);\n });\n try {\n await Promise.race([guarded, timeout]);\n } finally {\n if (timer) {\n clearTimeout(timer);\n }\n }\n}\n\nfunction newRequestId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (c?.randomUUID) {\n return c.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction mergeHeaders(\n defaults: Record<string, string>,\n overrides: Record<string, string> | undefined,\n): Record<string, string> {\n const merged: Record<string, string> = {};\n for (const [k, v] of Object.entries(defaults)) {\n merged[k.toLowerCase()] = v;\n }\n if (overrides) {\n for (const [k, v] of Object.entries(overrides)) {\n merged[k.toLowerCase()] = v;\n }\n }\n return merged;\n}\n\nfunction linkTimeoutSignal(\n parent: AbortSignal | undefined,\n timeoutMs: number,\n): { signal: AbortSignal; cancel: () => void } {\n const controller = new AbortController();\n const onParentAbort = () => {\n controller.abort(parent?.reason);\n };\n if (parent) {\n if (parent.aborted) {\n controller.abort(parent.reason);\n } else {\n parent.addEventListener('abort', onParentAbort, { once: true });\n }\n }\n const timer = setTimeout(() => {\n controller.abort(new Error(`Request timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n return {\n signal: controller.signal,\n cancel: () => {\n clearTimeout(timer);\n if (parent) {\n parent.removeEventListener('abort', onParentAbort);\n }\n },\n };\n}\n\nasync function readBody(res: Response, parseJson: boolean): Promise<unknown> {\n if (res.status === 204 || res.status === 205) {\n return null;\n }\n const contentType = res.headers.get('content-type') ?? '';\n if (parseJson && contentType.includes('application/json')) {\n const text = await res.text();\n if (text.length === 0) {\n return null;\n }\n return JSON.parse(text);\n }\n return res.text();\n}\n\nexport async function request<T = unknown>(\n req: HttpRequest,\n options: RequestOptions,\n): Promise<HttpResponse<T>> {\n const fetchImpl: FetchLike = options.fetch ?? (globalThis.fetch as FetchLike);\n const retry = req.retry ?? {};\n const maxAttempts = retry.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;\n const initialDelayMs = retry.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;\n const maxDelayMs = retry.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;\n const retryOn = retry.retryOn ?? defaultRetryOn;\n const timeoutMs = req.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const parseJson = req.parseJson ?? true;\n\n const headers = mergeHeaders(\n {\n 'User-Agent': DEFAULT_USER_AGENT,\n Accept: 'application/json',\n },\n req.headers,\n );\n\n let lastErr: Error | undefined;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n req.signal?.throwIfAborted();\n\n const { signal, cancel } = linkTimeoutSignal(req.signal, timeoutMs);\n let res: Response;\n try {\n res = await fetchImpl(req.url, {\n method: req.method ?? 'GET',\n headers,\n body: req.body as RequestInit['body'],\n signal,\n });\n } catch (err) {\n cancel();\n if (req.signal?.aborted) {\n throw req.signal.reason ?? err;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n lastErr = error;\n if (attempt < maxAttempts - 1 && retryOn(null, error)) {\n const delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n await sleep(delay, req.signal);\n continue;\n }\n throw new TransientError(error.message);\n }\n cancel();\n\n const body = await readBody(res, parseJson);\n const httpResponse: HttpResponse<T> = {\n status: res.status,\n headers: res.headers,\n body: body as T,\n };\n if (req.rateLimit) {\n const state = req.rateLimit.parse(res.headers);\n if (state) {\n httpResponse.rateLimitState = state;\n }\n }\n\n if (options.observer) {\n await notifyObserver(options.observer, {\n url: req.url,\n method: req.method ?? 'GET',\n status: res.status,\n resource: options.resource,\n requestId: options.requestId ?? newRequestId(),\n body,\n });\n }\n\n if (res.ok) {\n return httpResponse;\n }\n\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\n const message = `HTTP ${res.status} ${res.statusText} for ${req.method ?? 'GET'} ${req.url}`;\n const err = errorForStatus(message, httpResponse, retryAfter);\n\n if (\n attempt < maxAttempts - 1 &&\n retryOn(res.status, err) &&\n !(err instanceof AuthError) &&\n !(err instanceof ClientBugError)\n ) {\n lastErr = err;\n let delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n if (err instanceof RateLimitError && retryAfter) {\n const wait = retryAfter.getTime() - Date.now();\n if (wait > 0) {\n delay = Math.min(wait, maxDelayMs);\n }\n }\n await sleep(delay, req.signal);\n continue;\n }\n\n throw err;\n }\n\n throw lastErr ?? new UpstreamBugError('Exhausted retry attempts');\n}\n\nfunction computeDelay(\n attempt: number,\n initialDelayMs: number,\n maxDelayMs: number,\n): number {\n const base = initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, maxDelayMs);\n}\n\nexport { HttpClientError };\n","export interface RateLimitState {\n remaining: number;\n resetAt: Date;\n}\n\nexport interface RateLimitPolicy {\n parse(headers: Headers): RateLimitState | null;\n}\n\nexport interface StandardRateLimitPolicyConfig {\n remainingHeader: string;\n resetHeader: string;\n resetUnit: 's' | 'ms';\n resetFallbackMs?: number;\n}\n\nexport function standardRateLimitPolicy(\n config: StandardRateLimitPolicyConfig,\n): RateLimitPolicy {\n const { remainingHeader, resetHeader, resetUnit, resetFallbackMs } = config;\n const multiplier = resetUnit === 's' ? 1000 : 1;\n return {\n parse(h) {\n const remainingRaw = h.get(remainingHeader);\n if (remainingRaw === null || remainingRaw.trim() === '') {\n return null;\n }\n const remaining = Number(remainingRaw);\n if (!Number.isFinite(remaining)) {\n return null;\n }\n const resetRaw = h.get(resetHeader);\n if (resetRaw === null) {\n if (resetFallbackMs === undefined) {\n return null;\n }\n return {\n remaining,\n resetAt: new Date(Date.now() + resetFallbackMs),\n };\n }\n if (resetRaw.trim() === '') {\n return null;\n }\n const reset = Number(resetRaw);\n if (!Number.isFinite(reset) || reset < 0) {\n return null;\n }\n const resetMs = reset * multiplier;\n if (!Number.isFinite(resetMs)) {\n return null;\n }\n return { remaining, resetAt: new Date(resetMs) };\n },\n };\n}\n","export async function mapWithConcurrency<T, R>(\n items: readonly T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n if (items.length === 0) {\n return results;\n }\n const normalized = Number.isFinite(concurrency) ? Math.floor(concurrency) : 1;\n const limit = Math.max(1, Math.min(normalized, items.length));\n let next = 0;\n let failed = false;\n\n async function worker(): Promise<void> {\n while (!failed) {\n const i = next++;\n if (i >= items.length) {\n return;\n }\n try {\n results[i] = await fn(items[i]!, i);\n } catch (err) {\n failed = true;\n throw err;\n }\n }\n }\n\n const workers: Promise<void>[] = [];\n for (let w = 0; w < limit; w++) {\n workers.push(worker());\n }\n await Promise.all(workers);\n return results;\n}\n","export interface SanitizeAllowedUrlOptions {\n url: string | null;\n host: string;\n pathname: string;\n protocol?: 'https:' | 'http:';\n}\n\nexport function sanitizeAllowedUrl(\n options: SanitizeAllowedUrlOptions,\n): string | null {\n const { url, host, pathname, protocol = 'https:' } = options;\n if (url === null) {\n return null;\n }\n try {\n const u = new URL(url);\n if (u.protocol !== protocol || u.host !== host || u.pathname !== pathname) {\n return null;\n }\n return u.toString();\n } catch {\n return null;\n }\n}\n","export type EpochUnit = 'ms' | 's' | 'iso';\n\nexport function parseEpoch(\n value: number | string | null | undefined,\n unit: EpochUnit,\n): number | null {\n if (value === null || value === undefined) {\n return null;\n }\n if (unit === 'iso') {\n if (typeof value !== 'string') {\n return null;\n }\n const ms = new Date(value).getTime();\n return Number.isFinite(ms) ? ms : null;\n }\n if (typeof value === 'string' && value.trim() === '') {\n return null;\n }\n const n = typeof value === 'number' ? value : Number(value);\n if (!Number.isFinite(n)) {\n return null;\n }\n const result = unit === 's' ? n * 1000 : n;\n return Number.isFinite(result) ? result : null;\n}\n","import { request } from './request';\nimport type { HttpRequest } from './types';\n\nexport function parseLinkHeader(header: string | null): Record<string, string> {\n if (!header) {\n return {};\n }\n const result: Record<string, string> = {};\n for (const part of header.split(',')) {\n const match = part.match(/<([^>]+)>\\s*;\\s*rel=\"([^\"]+)\"/);\n if (match) {\n result[match[2]!] = match[1]!;\n }\n }\n return result;\n}\n\nexport async function* paginateLink<T>(\n initial: HttpRequest,\n parse: (body: unknown) => T[],\n options: { resource: string },\n): AsyncIterable<T> {\n let next: string | null = initial.url;\n while (next) {\n const res: Awaited<ReturnType<typeof request>> = await request(\n {\n ...initial,\n url: next,\n },\n { resource: options.resource },\n );\n for (const item of parse(res.body)) {\n yield item;\n }\n const links = parseLinkHeader(res.headers.get('link'));\n next = links['next'] ?? null;\n }\n}\n\nexport async function* paginateCursor<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; nextCursor: string | null },\n buildNext: (req: HttpRequest, cursor: string) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let req: HttpRequest = initial;\n while (true) {\n const res = await request(req, { resource: options.resource });\n const { items, nextCursor } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!nextCursor) {\n return;\n }\n req = buildNext(req, nextCursor);\n }\n}\n\nexport async function* paginatePage<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; hasMore: boolean },\n buildPage: (req: HttpRequest, page: number) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let page = 1;\n while (true) {\n const req = page === 1 ? initial : buildPage(initial, page);\n const res = await request(req, { resource: options.resource });\n const { items, hasMore } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!hasMore || items.length === 0) {\n return;\n }\n page++;\n }\n}\n","export type LogFields = Record<string, unknown>;\n\nexport interface ConnectorLogger {\n info(event: string, fields?: LogFields): void;\n warn(event: string, fields?: LogFields): void;\n}\n\nexport interface ConnectorLoggerOptions {\n scope: string;\n}\n\nconst MAX_VALUE_LEN = 120;\n\nfunction truncate(s: string, max = MAX_VALUE_LEN): string {\n if (s.length <= max) {\n return s;\n }\n return `${s.slice(0, max - 1)}…`;\n}\n\nfunction formatValue(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (value === undefined) {\n return '';\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n if (typeof value === 'string') {\n const t = truncate(value);\n if (/[\\s\"=]/.test(t)) {\n return JSON.stringify(t);\n }\n return t;\n }\n if (typeof value === 'bigint') {\n return value.toString();\n }\n let json: string | undefined;\n try {\n json = JSON.stringify(value);\n } catch {\n json = undefined;\n }\n return truncate(json ?? String(value));\n}\n\nexport function formatLogFields(fields?: LogFields): string {\n if (!fields) {\n return '';\n }\n const parts: string[] = [];\n for (const [k, v] of Object.entries(fields)) {\n if (v === undefined) {\n continue;\n }\n parts.push(`${k}=${formatValue(v)}`);\n }\n return parts.length > 0 ? ` ${parts.join(' ')}` : '';\n}\n\nexport function formatLogLine(\n scope: string,\n event: string,\n fields?: LogFields,\n): string {\n return `[${scope}] ${event}${formatLogFields(fields)}`;\n}\n\nexport function createDefaultConnectorLogger(\n opts: ConnectorLoggerOptions,\n): ConnectorLogger {\n return {\n info(event, fields) {\n console.info(formatLogLine(opts.scope, event, fields));\n },\n warn(event, fields) {\n console.warn(formatLogLine(opts.scope, event, fields));\n },\n };\n}\n\nconst NOOP_LOGGER: ConnectorLogger = {\n info() {},\n warn() {},\n};\n\nexport function noopConnectorLogger(): ConnectorLogger {\n return NOOP_LOGGER;\n}\n","import {\n type HttpResponse,\n connectorUserAgent,\n parseEpoch,\n} from '@rawdash/connector-shared';\nimport {\n BaseConnector,\n type ChunkedSyncCursor,\n type ConnectorContext,\n type ConnectorDoc,\n type CredentialsSchema,\n type JSONValue,\n type StorageHandle,\n type SyncOptions,\n type SyncResult,\n defineConfigFields,\n defineConnectorDoc,\n defineResources,\n makeChunkedCursorGuard,\n paginateChunked,\n schemasFromResources,\n selectActivePhases,\n} from '@rawdash/core';\nimport { z } from 'zod';\n\nconst nrqlQuerySchema = z.object({\n name: z\n .string()\n .min(1)\n .regex(/^[a-zA-Z0-9_]+$/, {\n message: 'Metric name must be alphanumeric / underscore',\n }),\n query: z.string().min(1),\n});\n\nexport const configFields = defineConfigFields(\n z.object({\n apiKey: z.object({ $secret: z.string().min(1) }).meta({\n label: 'User API Key',\n description:\n 'New Relic User API key. Create at New Relic -> API keys (User key type, ingest-keys do not work for NerdGraph).',\n placeholder: 'NRAK-...',\n secret: true,\n }),\n accountId: z.number().int().positive().meta({\n label: 'Account ID',\n description:\n 'New Relic account ID the User API key has access to. Find it under New Relic -> Administration -> Access management.',\n placeholder: '1234567',\n }),\n region: z.enum(['US', 'EU']).optional().meta({\n label: 'Region',\n description:\n 'New Relic data region. Defaults to `US` (`api.newrelic.com`); set to `EU` to use `api.eu.newrelic.com`.',\n placeholder: 'US',\n }),\n nrqlQueries: z.array(nrqlQuerySchema).nonempty().optional().meta({\n label: 'NRQL queries (optional)',\n description:\n 'User-declared NRQL queries. Each entry produces `newrelic_nrql_metric` samples named `<name>` from the NerdGraph NRQL API.',\n }),\n resources: z\n .array(z.enum(['alerts', 'alert_violations', 'nrql_queries']))\n .nonempty()\n .optional()\n .meta({\n label: 'Resources',\n description:\n 'Which New Relic resources to sync. Omit to sync all of them.',\n }),\n incidentsLookbackHours: z\n .number()\n .int()\n .positive()\n .max(720)\n .optional()\n .meta({\n label: 'Incidents lookback (hours)',\n description:\n 'Window of NrAiIncident rows to pull on each sync, in hours. Defaults to 168 (7 days). Ignored when `since` is set by the host.',\n placeholder: '168',\n }),\n metricsLookbackHours: z.number().int().positive().max(168).optional().meta({\n label: 'NRQL metrics lookback (hours)',\n description:\n 'Window of NRQL metric samples to pull on each sync, in hours. Defaults to 24. Each user query gets `SINCE <lookback> hours ago` appended unless the query already declares its own `SINCE` clause.',\n placeholder: '24',\n }),\n }),\n);\n\nexport const doc: ConnectorDoc = defineConnectorDoc({\n displayName: 'New Relic',\n category: 'infrastructure',\n brandColor: '#1CE783',\n tagline:\n 'Sync NRQL alert conditions, AI incidents, and user-declared NRQL metric queries from a New Relic account via NerdGraph.',\n vendor: {\n name: 'New Relic',\n domain: 'newrelic.com',\n apiDocs: 'https://docs.newrelic.com/docs/apis/nerdgraph/',\n website: 'https://newrelic.com',\n },\n auth: {\n summary:\n 'A New Relic User API key plus the numeric account ID are required. The key is stored as a secret and used to authenticate every NerdGraph GraphQL request.',\n setup: [\n 'Open New Relic -> API keys and create a `User` key. Ingest-keys are not accepted by NerdGraph.',\n 'Find the numeric account ID under New Relic -> Administration -> Access management. The User key must have access to that account.',\n 'Store the User key as a secret and reference it from the connector config as `apiKey: secret(\"NEWRELIC_USER_KEY\")`.',\n 'Set `accountId` to the numeric account ID, and optionally `region: \"EU\"` if the data lives on the EU host (`api.eu.newrelic.com`); defaults to `US` (`api.newrelic.com`).',\n ],\n },\n rateLimit:\n 'NerdGraph enforces per-account NRQL quotas; this connector retries on 429s through the standard HTTP retry policy.',\n limitations: [\n 'APM-trace deep inspection is out of scope (not dashboard-shaped).',\n 'NRQL queries are single-shot per sync (NRQL does not support cursor pagination); large queries should narrow their `SINCE` window or use `LIMIT MAX`.',\n 'Incidents are pulled via NRQL on the NrAiIncident event type, which depends on Applied Intelligence being enabled on the account.',\n 'Only NRQL-based alert conditions are synced; legacy V1 condition types are not exposed.',\n ],\n});\n\nexport type NewRelicResource = 'alerts' | 'alert_violations' | 'nrql_queries';\n\nexport interface NewRelicNrqlQuery {\n name: string;\n query: string;\n}\n\nexport interface NewRelicSettings {\n accountId: number;\n region?: 'US' | 'EU';\n nrqlQueries?: readonly NewRelicNrqlQuery[];\n resources?: readonly NewRelicResource[];\n incidentsLookbackHours?: number;\n metricsLookbackHours?: number;\n}\n\nconst newRelicCredentials = {\n apiKey: {\n description: 'New Relic User API key',\n auth: 'required' as const,\n },\n} satisfies CredentialsSchema;\n\ntype NewRelicCredentials = typeof newRelicCredentials;\n\nconst PHASE_ORDER = ['alert_conditions', 'incidents', 'metrics'] as const;\n\ntype NewRelicPhase = (typeof PHASE_ORDER)[number];\n\ntype NewRelicSyncCursor = ChunkedSyncCursor<NewRelicPhase, string>;\n\nconst isNewRelicSyncCursor = makeChunkedCursorGuard(PHASE_ORDER);\n\ninterface NrqlConditionNode {\n id: string;\n name: string;\n enabled: boolean;\n policyId: string;\n type: string;\n createdAt: number | null;\n updatedAt: number | null;\n nrql?: { query: string } | null;\n}\n\ninterface NrqlConditionsSearchResult {\n nrqlConditions: NrqlConditionNode[];\n nextCursor: string | null;\n totalCount: number;\n}\n\ninterface NrqlResult {\n results: Array<Record<string, JSONValue>>;\n metadata?: {\n facets?: string[] | null;\n timeWindow?: {\n begin?: number | null;\n end?: number | null;\n } | null;\n } | null;\n}\n\ninterface AlertConditionsResponse {\n actor: {\n account: {\n alerts: {\n nrqlConditionsSearch: NrqlConditionsSearchResult;\n };\n };\n };\n}\n\ninterface NrqlResponse {\n actor: {\n account: {\n nrql: NrqlResult;\n };\n };\n}\n\ninterface MetricsBatchItem {\n queryName: string;\n query: string;\n result: NrqlResult;\n}\n\ninterface GraphQLError {\n message: string;\n path?: Array<string | number>;\n}\n\ninterface GraphQLResponse<T> {\n data?: T;\n errors?: GraphQLError[];\n}\n\nconst ALERT_CONDITIONS_QUERY = `\n query AlertConditions($accountId: Int!, $cursor: String) {\n actor {\n account(id: $accountId) {\n alerts {\n nrqlConditionsSearch(cursor: $cursor) {\n nrqlConditions {\n id\n name\n enabled\n policyId\n type\n createdAt\n updatedAt\n nrql { query }\n }\n nextCursor\n totalCount\n }\n }\n }\n }\n }\n`;\n\nconst NRQL_QUERY = `\n query RunNrql($accountId: Int!, $query: Nrql!) {\n actor {\n account(id: $accountId) {\n nrql(query: $query) {\n results\n metadata {\n facets\n timeWindow {\n begin\n end\n }\n }\n }\n }\n }\n }\n`;\n\nconst idString = z.string().min(1);\nconst nrqlScalarSchema = z.union([\n z.string(),\n z.number(),\n z.boolean(),\n z.null(),\n]);\nconst nrqlCellSchema = z.union([nrqlScalarSchema, z.array(nrqlScalarSchema)]);\n\nconst nrqlConditionNodeSchema = z.object({\n id: idString,\n name: z.string(),\n enabled: z.boolean(),\n policyId: idString,\n type: z.string(),\n createdAt: z.number().nullable(),\n updatedAt: z.number().nullable(),\n nrql: z.object({ query: z.string() }).nullable().optional(),\n});\n\nconst nrqlConditionsSearchResultSchema = z.object({\n nrqlConditions: z.array(nrqlConditionNodeSchema),\n nextCursor: z.string().nullable(),\n totalCount: z.number().int().nonnegative(),\n});\n\nconst nrqlResultSchema = z.object({\n results: z.array(z.record(z.string(), nrqlCellSchema)),\n metadata: z\n .object({\n facets: z.array(z.string()).nullable().optional(),\n timeWindow: z\n .object({\n begin: z.number().nullable().optional(),\n end: z.number().nullable().optional(),\n })\n .nullable()\n .optional(),\n })\n .nullable()\n .optional(),\n});\n\nconst DEFAULT_REGION: 'US' | 'EU' = 'US';\nconst DEFAULT_INCIDENTS_LOOKBACK_HOURS = 168;\nconst DEFAULT_METRICS_LOOKBACK_HOURS = 24;\nconst INCIDENTS_NRQL_LIMIT = 5000;\nconst TIMESTAMP_FIELDS = new Set([\n 'timestamp',\n 'beginTimeSeconds',\n 'endTimeSeconds',\n 'begin_time',\n 'end_time',\n 'beginTime',\n 'endTime',\n 'openedAt',\n 'closedAt',\n]);\nconst FACET_FIELDS = new Set(['facet']);\n\nexport const newRelicResources = defineResources({\n newrelic_alert_condition: {\n shape: 'entity',\n description:\n 'NRQL alert conditions with name, enabled state, policy id, type, and the underlying NRQL query string.',\n endpoint:\n 'GraphQL query: actor.account.alerts.nrqlConditionsSearch { nrqlConditions { ... } }',\n responses: { alert_conditions: nrqlConditionsSearchResultSchema },\n },\n newrelic_alert_violation: {\n shape: 'event',\n description:\n 'AI alert violation events. Each row from the NrAiIncident event type becomes one event with openedAt / closedAt and the underlying condition / policy metadata.',\n endpoint:\n 'GraphQL nrql() against SELECT ... FROM NrAiIncident WHERE openedAt > ...',\n notes:\n 'Append-only across syncs; the connector filters NrAiIncident by `openedAt` against `options.since` (or the configured lookback) to avoid re-emitting old incidents.',\n responses: { incidents: nrqlResultSchema },\n },\n newrelic_nrql_metric: {\n shape: 'metric',\n dynamic: true,\n description:\n 'User-declared NRQL metric samples, stored as `newrelic_nrql_metric.<query name>`. Each NRQL result row is mapped to a single sample using the first numeric, non-timestamp/facet field as the value.',\n endpoint: 'GraphQL nrql() against the user-declared NRQL query',\n dimensions: [\n { name: 'queryName', description: 'The user-declared query name.' },\n { name: 'query', description: 'The NRQL query string.' },\n {\n name: 'facets',\n description:\n 'Comma-joined facet values for the row, or `*` when the result row is ungrouped.',\n },\n ],\n responses: { nrql_queries: nrqlResultSchema },\n },\n});\n\nexport const id = 'new-relic';\n\nexport class NewRelicConnector extends BaseConnector<\n NewRelicSettings,\n NewRelicCredentials\n> {\n static readonly id = id;\n\n static readonly resources = newRelicResources;\n\n static readonly schemas = schemasFromResources(newRelicResources);\n\n static create(input: unknown, ctx?: ConnectorContext): NewRelicConnector {\n const parsed = configFields.parse(input);\n return new NewRelicConnector(\n {\n accountId: parsed.accountId,\n region: parsed.region,\n nrqlQueries: parsed.nrqlQueries,\n resources: parsed.resources,\n incidentsLookbackHours: parsed.incidentsLookbackHours,\n metricsLookbackHours: parsed.metricsLookbackHours,\n },\n { apiKey: parsed.apiKey },\n ctx,\n );\n }\n\n readonly id = id;\n override readonly credentials = newRelicCredentials;\n\n private get endpoint(): string {\n const region = this.settings.region ?? DEFAULT_REGION;\n return region === 'EU'\n ? 'https://api.eu.newrelic.com/graphql'\n : 'https://api.newrelic.com/graphql';\n }\n\n private buildHeaders(): Record<string, string> {\n return {\n 'API-Key': this.creds.apiKey,\n 'Content-Type': 'application/json',\n 'User-Agent': connectorUserAgent('new-relic'),\n };\n }\n\n private async graphql<T>(\n query: string,\n variables: Record<string, unknown>,\n resource: string,\n signal?: AbortSignal,\n ): Promise<HttpResponse<GraphQLResponse<T>>> {\n const res = await this.post<GraphQLResponse<T>>(this.endpoint, {\n resource,\n headers: this.buildHeaders(),\n body: JSON.stringify({ query, variables }),\n signal,\n });\n if (res.body.errors && res.body.errors.length > 0) {\n const messages = res.body.errors.map((e) => e.message).join('; ');\n throw new Error(`New Relic NerdGraph error: ${messages}`);\n }\n if (!res.body.data) {\n throw new Error(\n `New Relic NerdGraph response missing data for resource '${resource}'`,\n );\n }\n return res;\n }\n\n private activePhases(): NewRelicPhase[] {\n return selectActivePhases<NewRelicResource, NewRelicPhase>(\n (r) => {\n switch (r) {\n case 'alerts':\n return 'alert_conditions';\n case 'alert_violations':\n return 'incidents';\n case 'nrql_queries':\n return 'metrics';\n }\n },\n PHASE_ORDER,\n this.settings.resources,\n );\n }\n\n private async fetchAlertConditionsPage(\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<{ items: NrqlConditionNode[]; next: string | null }> {\n const res = await this.graphql<AlertConditionsResponse>(\n ALERT_CONDITIONS_QUERY,\n { accountId: this.settings.accountId, cursor: page },\n 'alert_conditions',\n signal,\n );\n const search = res.body.data!.actor.account.alerts.nrqlConditionsSearch;\n return {\n items: search.nrqlConditions,\n next: search.nextCursor,\n };\n }\n\n private async fetchIncidents(\n options: SyncOptions,\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<{ items: Array<Record<string, JSONValue>>; next: string | null }> {\n const sinceMs = options.since ? parseEpoch(options.since, 'iso') : null;\n const lookbackHours =\n this.settings.incidentsLookbackHours ?? DEFAULT_INCIDENTS_LOOKBACK_HOURS;\n const fromMs = sinceMs ?? Date.now() - lookbackHours * 60 * 60 * 1000;\n const floorMs = page !== null ? Number(page) : fromMs;\n const nrql = `SELECT incidentId, conditionFamilyId, policyName, conditionName, openedAt, closedAt, durationSeconds, priority, title, state, entityGuid FROM NrAiIncident WHERE openedAt > ${floorMs} ORDER BY openedAt ASC LIMIT ${INCIDENTS_NRQL_LIMIT}`;\n const res = await this.graphql<NrqlResponse>(\n NRQL_QUERY,\n { accountId: this.settings.accountId, query: nrql },\n 'incidents',\n signal,\n );\n const results = res.body.data!.actor.account.nrql.results;\n if (results.length < INCIDENTS_NRQL_LIMIT) {\n return { items: results, next: null };\n }\n const lastOpenedAt = results[results.length - 1]?.openedAt;\n const next =\n typeof lastOpenedAt === 'number' &&\n Number.isFinite(lastOpenedAt) &&\n lastOpenedAt > floorMs\n ? String(lastOpenedAt)\n : null;\n return { items: results, next };\n }\n\n private async fetchMetrics(\n options: SyncOptions,\n signal: AbortSignal | undefined,\n ): Promise<{ items: MetricsBatchItem[]; next: string | null }> {\n const queries = this.settings.nrqlQueries ?? [];\n if (queries.length === 0) {\n return { items: [], next: null };\n }\n const lookbackHours =\n this.settings.metricsLookbackHours ?? DEFAULT_METRICS_LOOKBACK_HOURS;\n const items: MetricsBatchItem[] = [];\n for (const q of queries) {\n signal?.throwIfAborted();\n const nrql = this.appendSinceIfMissing(\n q.query,\n options.since ?? null,\n lookbackHours,\n );\n const res = await this.graphql<NrqlResponse>(\n NRQL_QUERY,\n { accountId: this.settings.accountId, query: nrql },\n 'nrql_queries',\n signal,\n );\n items.push({\n queryName: q.name,\n query: q.query,\n result: res.body.data!.actor.account.nrql,\n });\n }\n return { items, next: null };\n }\n\n private appendSinceIfMissing(\n query: string,\n since: string | null,\n fallbackHours: number,\n ): string {\n if (/\\bSINCE\\b/i.test(query)) {\n return query;\n }\n const sinceMs = since ? parseEpoch(since, 'iso') : null;\n if (sinceMs !== null) {\n return `${query} SINCE ${sinceMs}`;\n }\n return `${query} SINCE ${fallbackHours} hours ago`;\n }\n\n private async writeAlertConditions(\n storage: StorageHandle,\n conditions: NrqlConditionNode[],\n ): Promise<void> {\n for (const c of conditions) {\n const createdMs = c.createdAt ?? null;\n const modifiedMs = c.updatedAt ?? null;\n const updatedAt = modifiedMs ?? createdMs ?? Date.now();\n await storage.entity({\n type: 'newrelic_alert_condition',\n id: c.id,\n attributes: {\n conditionId: c.id,\n name: c.name,\n enabled: c.enabled,\n policyId: c.policyId,\n conditionType: c.type,\n nrqlQuery: c.nrql?.query ?? null,\n createdAt: createdMs,\n modifiedAt: modifiedMs,\n },\n updated_at: updatedAt,\n });\n }\n }\n\n private async writeIncidents(\n storage: StorageHandle,\n rows: Array<Record<string, JSONValue>>,\n ): Promise<void> {\n for (const row of rows) {\n const incidentId =\n typeof row.incidentId === 'string' || typeof row.incidentId === 'number'\n ? String(row.incidentId)\n : null;\n const openedAtRaw = row.openedAt;\n const openedAtMs =\n typeof openedAtRaw === 'number' && Number.isFinite(openedAtRaw)\n ? openedAtRaw\n : null;\n if (incidentId === null || openedAtMs === null) {\n continue;\n }\n const closedAtRaw = row.closedAt;\n const closedAtMs =\n typeof closedAtRaw === 'number' && Number.isFinite(closedAtRaw)\n ? closedAtRaw\n : null;\n await storage.event({\n name: 'newrelic_alert_violation',\n start_ts: openedAtMs,\n end_ts: closedAtMs,\n attributes: {\n incidentId,\n conditionFamilyId: this.coerceScalar(row.conditionFamilyId),\n conditionName: this.coerceScalar(row.conditionName),\n policyName: this.coerceScalar(row.policyName),\n priority: this.coerceScalar(row.priority),\n title: this.coerceScalar(row.title),\n state: this.coerceScalar(row.state),\n entityGuid: this.coerceScalar(row.entityGuid),\n durationSeconds: this.coerceScalar(row.durationSeconds),\n },\n });\n }\n }\n\n private coerceScalar(value: JSONValue | undefined): JSONValue {\n if (value === undefined) {\n return null;\n }\n return value;\n }\n\n private async writeMetrics(\n storage: StorageHandle,\n items: MetricsBatchItem[],\n ): Promise<void> {\n if (items.length === 0) {\n return;\n }\n const samplesByName: Map<\n string,\n Array<{\n name: string;\n ts: number;\n value: number;\n attributes: Record<string, string | number>;\n }>\n > = new Map();\n for (const item of items) {\n const facets = item.result.metadata?.facets ?? [];\n for (const row of item.result.results) {\n const ts = this.extractTimestamp(row, item.result);\n if (ts === null) {\n continue;\n }\n const value = this.extractValue(row);\n if (value === null) {\n continue;\n }\n const facetsStr = this.extractFacets(row, facets ?? []);\n const name = `newrelic_nrql_metric.${item.queryName}`;\n let bucket = samplesByName.get(name);\n if (!bucket) {\n bucket = [];\n samplesByName.set(name, bucket);\n }\n bucket.push({\n name,\n ts,\n value,\n attributes: {\n queryName: item.queryName,\n query: item.query,\n facets: facetsStr,\n },\n });\n }\n }\n for (const [name, samples] of samplesByName) {\n await storage.metrics(samples, { names: [name] });\n }\n }\n\n private extractTimestamp(\n row: Record<string, JSONValue>,\n result: NrqlResult,\n ): number | null {\n const endTimeSeconds = row.endTimeSeconds;\n if (typeof endTimeSeconds === 'number' && Number.isFinite(endTimeSeconds)) {\n return parseEpoch(endTimeSeconds, 's');\n }\n const beginTimeSeconds = row.beginTimeSeconds;\n if (\n typeof beginTimeSeconds === 'number' &&\n Number.isFinite(beginTimeSeconds)\n ) {\n return parseEpoch(beginTimeSeconds, 's');\n }\n const timestamp = row.timestamp;\n if (typeof timestamp === 'number' && Number.isFinite(timestamp)) {\n return parseEpoch(timestamp, 'ms');\n }\n const endMs = result.metadata?.timeWindow?.end;\n if (typeof endMs === 'number' && Number.isFinite(endMs)) {\n return parseEpoch(endMs, 'ms');\n }\n return Date.now();\n }\n\n private extractValue(row: Record<string, JSONValue>): number | null {\n for (const [key, value] of Object.entries(row)) {\n if (TIMESTAMP_FIELDS.has(key) || FACET_FIELDS.has(key)) {\n continue;\n }\n if (typeof value === 'number' && Number.isFinite(value)) {\n return value;\n }\n }\n return null;\n }\n\n private extractFacets(\n row: Record<string, JSONValue>,\n facetNames: string[],\n ): string {\n const facetRaw = row.facet;\n if (Array.isArray(facetRaw)) {\n return facetRaw.length > 0 ? facetRaw.map(String).join(',') : '*';\n }\n if (typeof facetRaw === 'string' && facetRaw.length > 0) {\n return facetRaw;\n }\n if (facetNames.length > 0) {\n const parts: string[] = [];\n for (const fname of facetNames) {\n const v = row[fname];\n if (v !== undefined && v !== null) {\n parts.push(String(v));\n }\n }\n if (parts.length > 0) {\n return parts.join(',');\n }\n }\n return '*';\n }\n\n async sync(\n options: SyncOptions,\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<SyncResult> {\n const cursor: NewRelicSyncCursor | undefined = isNewRelicSyncCursor(\n options.cursor,\n )\n ? options.cursor\n : undefined;\n const isFull = options.mode === 'full';\n const phases = this.activePhases();\n\n return paginateChunked<NewRelicPhase, string>({\n phases,\n cursor,\n signal,\n logger: this.logger,\n fetchPage: async (phase, page, sig) => {\n switch (phase) {\n case 'alert_conditions':\n return this.fetchAlertConditionsPage(page, sig);\n case 'incidents':\n return this.fetchIncidents(options, page, sig);\n case 'metrics':\n return this.fetchMetrics(options, sig);\n }\n },\n writeBatch: async (phase, items, page) => {\n if (isFull && page === null) {\n switch (phase) {\n case 'alert_conditions':\n await storage.entities([], {\n types: ['newrelic_alert_condition'],\n });\n break;\n case 'incidents':\n await storage.events([], { names: ['newrelic_alert_violation'] });\n break;\n case 'metrics':\n for (const q of this.settings.nrqlQueries ?? []) {\n await storage.metrics([], {\n names: [`newrelic_nrql_metric.${q.name}`],\n });\n }\n break;\n }\n }\n switch (phase) {\n case 'alert_conditions':\n return this.writeAlertConditions(\n storage,\n items as NrqlConditionNode[],\n );\n case 'incidents':\n return this.writeIncidents(\n storage,\n items as Array<Record<string, JSONValue>>,\n );\n case 'metrics':\n return this.writeMetrics(storage, items as MetricsBatchItem[]);\n }\n },\n });\n }\n}\n","import { NewRelicConnector } from './new-relic';\n\nexport {\n configFields,\n doc,\n NewRelicConnector,\n id,\n newRelicResources as resources,\n} from './new-relic';\nexport type { NewRelicResource, NewRelicSettings } from './new-relic';\nexport default NewRelicConnector;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../connector-shared/src/errors.ts","../../../connector-shared/src/retry.ts","../../../connector-shared/src/version.ts","../../../connector-shared/src/request.ts","../../../connector-shared/src/rate-limit.ts","../../../connector-shared/src/map-concurrent.ts","../../../connector-shared/src/sanitize.ts","../../../connector-shared/src/epoch.ts","../../../connector-shared/src/pagination.ts","../../../connector-shared/src/logger.ts","../src/new-relic.ts","../src/index.ts"],"sourcesContent":["import type { HttpResponse } from './types';\n\nexport type HttpErrorKind =\n | 'transient'\n | 'rate_limit'\n | 'auth'\n | 'upstream_bug'\n | 'client_bug';\n\nexport abstract class HttpClientError extends Error {\n abstract readonly kind: HttpErrorKind;\n readonly response?: HttpResponse;\n\n constructor(message: string, response?: HttpResponse) {\n super(message);\n this.name = new.target.name;\n this.response = response;\n }\n}\n\nexport class TransientError extends HttpClientError {\n readonly kind = 'transient' as const;\n}\n\nexport class RateLimitError extends HttpClientError {\n readonly kind = 'rate_limit' as const;\n readonly retryAfter?: Date;\n\n constructor(message: string, response?: HttpResponse, retryAfter?: Date) {\n super(message, response);\n this.retryAfter = retryAfter;\n }\n}\n\nexport class AuthError extends HttpClientError {\n readonly kind = 'auth' as const;\n}\n\nexport class UpstreamBugError extends HttpClientError {\n readonly kind = 'upstream_bug' as const;\n}\n\nexport class ClientBugError extends HttpClientError {\n readonly kind = 'client_bug' as const;\n}\n\nexport function classifyStatus(status: number): HttpErrorKind {\n if (status === 429) {\n return 'rate_limit';\n }\n if (status === 401 || status === 403) {\n return 'auth';\n }\n if (status === 408) {\n return 'transient';\n }\n if (status >= 500) {\n return 'upstream_bug';\n }\n if (status >= 400) {\n return 'client_bug';\n }\n return 'client_bug';\n}\n\nexport function errorForStatus(\n message: string,\n response: HttpResponse,\n retryAfter?: Date,\n): HttpClientError {\n const kind = classifyStatus(response.status);\n switch (kind) {\n case 'rate_limit':\n return new RateLimitError(message, response, retryAfter);\n case 'auth':\n return new AuthError(message, response);\n case 'transient':\n return new TransientError(message, response);\n case 'upstream_bug':\n return new UpstreamBugError(message, response);\n case 'client_bug':\n return new ClientBugError(message, response);\n }\n}\n","import { HttpClientError, RateLimitError, TransientError } from './errors';\n\nexport interface RetryPolicy {\n maxAttempts?: number;\n initialDelayMs?: number;\n maxDelayMs?: number;\n retryOn?: (status: number | null, err?: Error) => boolean;\n}\n\nexport const defaultRetryOn = (status: number | null, err?: Error): boolean => {\n if (err instanceof RateLimitError) {\n return true;\n }\n if (err instanceof TransientError) {\n return true;\n }\n if (status === null) {\n return err instanceof Error && !(err instanceof HttpClientError);\n }\n if (status === 408 || status === 429) {\n return true;\n }\n if (status >= 500) {\n return true;\n }\n return false;\n};\n\nexport function backoffDelayMs(\n attempt: number,\n policy: Required<Pick<RetryPolicy, 'initialDelayMs' | 'maxDelayMs'>>,\n): number {\n const base = policy.initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, policy.maxDelayMs);\n}\n\nexport function parseRetryAfter(\n headerValue: string | null,\n now: Date = new Date(),\n): Date | undefined {\n if (!headerValue) {\n return undefined;\n }\n const trimmed = headerValue.trim();\n if (/^\\d+$/.test(trimmed)) {\n return new Date(now.getTime() + Number(trimmed) * 1000);\n }\n const parsed = Date.parse(trimmed);\n if (Number.isNaN(parsed)) {\n return undefined;\n }\n return new Date(parsed);\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n return Promise.reject(signal.reason ?? new Error('Aborted'));\n }\n return new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal!.reason ?? new Error('Aborted'));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n","export const HTTP_CLIENT_VERSION = '0.0.0';\n\nexport const DEFAULT_USER_AGENT = `rawdash-connector/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n\nexport function connectorUserAgent(connectorId: string): string {\n return `rawdash-connector-${connectorId}/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n}\n","import {\n AuthError,\n ClientBugError,\n HttpClientError,\n RateLimitError,\n TransientError,\n UpstreamBugError,\n errorForStatus,\n} from './errors';\nimport { defaultRetryOn, parseRetryAfter, sleep } from './retry';\nimport type { FetchLike, HttpMethod, HttpRequest, HttpResponse } from './types';\nimport { DEFAULT_USER_AGENT } from './version';\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_ATTEMPTS = 3;\nconst DEFAULT_INITIAL_DELAY_MS = 1000;\nconst DEFAULT_MAX_DELAY_MS = 60_000;\nconst OBSERVER_TIMEOUT_MS = 250;\n\nexport interface RequestObservation {\n url: string;\n method: HttpMethod;\n status: number;\n resource: string;\n requestId: string;\n body: unknown;\n}\n\nexport type RequestObserver = (\n event: RequestObservation,\n) => void | Promise<void>;\n\nexport interface RequestOptions {\n fetch?: FetchLike;\n observer?: RequestObserver;\n resource: string;\n requestId?: string;\n}\n\nasync function notifyObserver(\n observer: RequestObserver,\n event: RequestObservation,\n): Promise<void> {\n let result: void | Promise<void>;\n try {\n result = observer(event);\n } catch (err) {\n console.warn('[connector-shared] request observer threw:', err);\n return;\n }\n if (!(result instanceof Promise)) {\n return;\n }\n const guarded = result.catch((err) => {\n console.warn('[connector-shared] request observer rejected:', err);\n });\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, OBSERVER_TIMEOUT_MS);\n });\n try {\n await Promise.race([guarded, timeout]);\n } finally {\n if (timer) {\n clearTimeout(timer);\n }\n }\n}\n\nfunction newRequestId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (c?.randomUUID) {\n return c.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction mergeHeaders(\n defaults: Record<string, string>,\n overrides: Record<string, string> | undefined,\n): Record<string, string> {\n const merged: Record<string, string> = {};\n for (const [k, v] of Object.entries(defaults)) {\n merged[k.toLowerCase()] = v;\n }\n if (overrides) {\n for (const [k, v] of Object.entries(overrides)) {\n merged[k.toLowerCase()] = v;\n }\n }\n return merged;\n}\n\nfunction linkTimeoutSignal(\n parent: AbortSignal | undefined,\n timeoutMs: number,\n): { signal: AbortSignal; cancel: () => void } {\n const controller = new AbortController();\n const onParentAbort = () => {\n controller.abort(parent?.reason);\n };\n if (parent) {\n if (parent.aborted) {\n controller.abort(parent.reason);\n } else {\n parent.addEventListener('abort', onParentAbort, { once: true });\n }\n }\n const timer = setTimeout(() => {\n controller.abort(new Error(`Request timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n return {\n signal: controller.signal,\n cancel: () => {\n clearTimeout(timer);\n if (parent) {\n parent.removeEventListener('abort', onParentAbort);\n }\n },\n };\n}\n\nasync function readBody(res: Response, parseJson: boolean): Promise<unknown> {\n if (res.status === 204 || res.status === 205) {\n return null;\n }\n const contentType = res.headers.get('content-type') ?? '';\n if (parseJson && contentType.includes('application/json')) {\n const text = await res.text();\n if (text.length === 0) {\n return null;\n }\n return JSON.parse(text);\n }\n return res.text();\n}\n\nexport async function request<T = unknown>(\n req: HttpRequest,\n options: RequestOptions,\n): Promise<HttpResponse<T>> {\n const fetchImpl: FetchLike = options.fetch ?? (globalThis.fetch as FetchLike);\n const retry = req.retry ?? {};\n const maxAttempts = retry.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;\n const initialDelayMs = retry.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;\n const maxDelayMs = retry.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;\n const retryOn = retry.retryOn ?? defaultRetryOn;\n const timeoutMs = req.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const parseJson = req.parseJson ?? true;\n\n const headers = mergeHeaders(\n {\n 'User-Agent': DEFAULT_USER_AGENT,\n Accept: 'application/json',\n },\n req.headers,\n );\n\n let lastErr: Error | undefined;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n req.signal?.throwIfAborted();\n\n const { signal, cancel } = linkTimeoutSignal(req.signal, timeoutMs);\n let res: Response;\n try {\n res = await fetchImpl(req.url, {\n method: req.method ?? 'GET',\n headers,\n body: req.body as RequestInit['body'],\n signal,\n });\n } catch (err) {\n cancel();\n if (req.signal?.aborted) {\n throw req.signal.reason ?? err;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n lastErr = error;\n if (attempt < maxAttempts - 1 && retryOn(null, error)) {\n const delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n await sleep(delay, req.signal);\n continue;\n }\n throw new TransientError(error.message);\n }\n cancel();\n\n const body = await readBody(res, parseJson);\n const httpResponse: HttpResponse<T> = {\n status: res.status,\n headers: res.headers,\n body: body as T,\n };\n if (req.rateLimit) {\n const state = req.rateLimit.parse(res.headers);\n if (state) {\n httpResponse.rateLimitState = state;\n }\n }\n\n if (options.observer) {\n await notifyObserver(options.observer, {\n url: req.url,\n method: req.method ?? 'GET',\n status: res.status,\n resource: options.resource,\n requestId: options.requestId ?? newRequestId(),\n body,\n });\n }\n\n if (res.ok) {\n return httpResponse;\n }\n\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\n const message = `HTTP ${res.status} ${res.statusText} for ${req.method ?? 'GET'} ${req.url}`;\n const err = errorForStatus(message, httpResponse, retryAfter);\n\n if (\n attempt < maxAttempts - 1 &&\n retryOn(res.status, err) &&\n !(err instanceof AuthError) &&\n !(err instanceof ClientBugError)\n ) {\n lastErr = err;\n let delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n if (err instanceof RateLimitError && retryAfter) {\n const wait = retryAfter.getTime() - Date.now();\n if (wait > 0) {\n delay = Math.min(wait, maxDelayMs);\n }\n }\n await sleep(delay, req.signal);\n continue;\n }\n\n throw err;\n }\n\n throw lastErr ?? new UpstreamBugError('Exhausted retry attempts');\n}\n\nfunction computeDelay(\n attempt: number,\n initialDelayMs: number,\n maxDelayMs: number,\n): number {\n const base = initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, maxDelayMs);\n}\n\nexport { HttpClientError };\n","export interface RateLimitState {\n remaining: number;\n resetAt: Date;\n}\n\nexport interface RateLimitPolicy {\n parse(headers: Headers): RateLimitState | null;\n}\n\nexport interface StandardRateLimitPolicyConfig {\n remainingHeader: string;\n resetHeader: string;\n resetUnit: 's' | 'ms';\n resetFallbackMs?: number;\n}\n\nexport function standardRateLimitPolicy(\n config: StandardRateLimitPolicyConfig,\n): RateLimitPolicy {\n const { remainingHeader, resetHeader, resetUnit, resetFallbackMs } = config;\n const multiplier = resetUnit === 's' ? 1000 : 1;\n return {\n parse(h) {\n const remainingRaw = h.get(remainingHeader);\n if (remainingRaw === null || remainingRaw.trim() === '') {\n return null;\n }\n const remaining = Number(remainingRaw);\n if (!Number.isFinite(remaining)) {\n return null;\n }\n const resetRaw = h.get(resetHeader);\n if (resetRaw === null) {\n if (resetFallbackMs === undefined) {\n return null;\n }\n return {\n remaining,\n resetAt: new Date(Date.now() + resetFallbackMs),\n };\n }\n if (resetRaw.trim() === '') {\n return null;\n }\n const reset = Number(resetRaw);\n if (!Number.isFinite(reset) || reset < 0) {\n return null;\n }\n const resetMs = reset * multiplier;\n if (!Number.isFinite(resetMs)) {\n return null;\n }\n return { remaining, resetAt: new Date(resetMs) };\n },\n };\n}\n","export async function mapWithConcurrency<T, R>(\n items: readonly T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n if (items.length === 0) {\n return results;\n }\n const normalized = Number.isFinite(concurrency) ? Math.floor(concurrency) : 1;\n const limit = Math.max(1, Math.min(normalized, items.length));\n let next = 0;\n let failed = false;\n\n async function worker(): Promise<void> {\n while (!failed) {\n const i = next++;\n if (i >= items.length) {\n return;\n }\n try {\n results[i] = await fn(items[i]!, i);\n } catch (err) {\n failed = true;\n throw err;\n }\n }\n }\n\n const workers: Promise<void>[] = [];\n for (let w = 0; w < limit; w++) {\n workers.push(worker());\n }\n await Promise.all(workers);\n return results;\n}\n","export interface SanitizeAllowedUrlOptions {\n url: string | null;\n host: string;\n pathname: string;\n protocol?: 'https:' | 'http:';\n}\n\nexport function sanitizeAllowedUrl(\n options: SanitizeAllowedUrlOptions,\n): string | null {\n const { url, host, pathname, protocol = 'https:' } = options;\n if (url === null) {\n return null;\n }\n try {\n const u = new URL(url);\n if (u.protocol !== protocol || u.host !== host || u.pathname !== pathname) {\n return null;\n }\n return u.toString();\n } catch {\n return null;\n }\n}\n","export type EpochUnit = 'ms' | 's' | 'iso';\n\nexport function parseEpoch(\n value: number | string | null | undefined,\n unit: EpochUnit,\n): number | null {\n if (value === null || value === undefined) {\n return null;\n }\n if (unit === 'iso') {\n if (typeof value !== 'string') {\n return null;\n }\n const ms = new Date(value).getTime();\n return Number.isFinite(ms) ? ms : null;\n }\n if (typeof value === 'string' && value.trim() === '') {\n return null;\n }\n const n = typeof value === 'number' ? value : Number(value);\n if (!Number.isFinite(n)) {\n return null;\n }\n const result = unit === 's' ? n * 1000 : n;\n return Number.isFinite(result) ? result : null;\n}\n","import { request } from './request';\nimport type { HttpRequest } from './types';\n\nexport function parseLinkHeader(header: string | null): Record<string, string> {\n if (!header) {\n return {};\n }\n const result: Record<string, string> = {};\n for (const part of header.split(',')) {\n const match = part.match(/<([^>]+)>\\s*;\\s*rel=\"([^\"]+)\"/);\n if (match) {\n result[match[2]!] = match[1]!;\n }\n }\n return result;\n}\n\nexport async function* paginateLink<T>(\n initial: HttpRequest,\n parse: (body: unknown) => T[],\n options: { resource: string },\n): AsyncIterable<T> {\n let next: string | null = initial.url;\n while (next) {\n const res: Awaited<ReturnType<typeof request>> = await request(\n {\n ...initial,\n url: next,\n },\n { resource: options.resource },\n );\n for (const item of parse(res.body)) {\n yield item;\n }\n const links = parseLinkHeader(res.headers.get('link'));\n next = links['next'] ?? null;\n }\n}\n\nexport async function* paginateCursor<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; nextCursor: string | null },\n buildNext: (req: HttpRequest, cursor: string) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let req: HttpRequest = initial;\n while (true) {\n const res = await request(req, { resource: options.resource });\n const { items, nextCursor } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!nextCursor) {\n return;\n }\n req = buildNext(req, nextCursor);\n }\n}\n\nexport async function* paginatePage<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; hasMore: boolean },\n buildPage: (req: HttpRequest, page: number) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let page = 1;\n while (true) {\n const req = page === 1 ? initial : buildPage(initial, page);\n const res = await request(req, { resource: options.resource });\n const { items, hasMore } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!hasMore || items.length === 0) {\n return;\n }\n page++;\n }\n}\n","export type LogFields = Record<string, unknown>;\n\nexport interface ConnectorLogger {\n info(event: string, fields?: LogFields): void;\n warn(event: string, fields?: LogFields): void;\n}\n\nexport interface ConnectorLoggerOptions {\n scope: string;\n}\n\nconst MAX_VALUE_LEN = 120;\n\nfunction truncate(s: string, max = MAX_VALUE_LEN): string {\n if (s.length <= max) {\n return s;\n }\n return `${s.slice(0, max - 1)}…`;\n}\n\nfunction formatValue(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (value === undefined) {\n return '';\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n if (typeof value === 'string') {\n const t = truncate(value);\n if (/[\\s\"=]/.test(t)) {\n return JSON.stringify(t);\n }\n return t;\n }\n if (typeof value === 'bigint') {\n return value.toString();\n }\n let json: string | undefined;\n try {\n json = JSON.stringify(value);\n } catch {\n json = undefined;\n }\n return truncate(json ?? String(value));\n}\n\nexport function formatLogFields(fields?: LogFields): string {\n if (!fields) {\n return '';\n }\n const parts: string[] = [];\n for (const [k, v] of Object.entries(fields)) {\n if (v === undefined) {\n continue;\n }\n parts.push(`${k}=${formatValue(v)}`);\n }\n return parts.length > 0 ? ` ${parts.join(' ')}` : '';\n}\n\nexport function formatLogLine(\n scope: string,\n event: string,\n fields?: LogFields,\n): string {\n return `[${scope}] ${event}${formatLogFields(fields)}`;\n}\n\nexport function createDefaultConnectorLogger(\n opts: ConnectorLoggerOptions,\n): ConnectorLogger {\n return {\n info(event, fields) {\n console.info(formatLogLine(opts.scope, event, fields));\n },\n warn(event, fields) {\n console.warn(formatLogLine(opts.scope, event, fields));\n },\n };\n}\n\nconst NOOP_LOGGER: ConnectorLogger = {\n info() {},\n warn() {},\n};\n\nexport function noopConnectorLogger(): ConnectorLogger {\n return NOOP_LOGGER;\n}\n","import {\n type HttpResponse,\n connectorUserAgent,\n parseEpoch,\n} from '@rawdash/connector-shared';\nimport {\n BaseConnector,\n type ChunkedSyncCursor,\n type ConnectorContext,\n type ConnectorDoc,\n type CredentialsSchema,\n type JSONValue,\n type StorageHandle,\n type SyncOptions,\n type SyncResult,\n defineConfigFields,\n defineConnectorDoc,\n defineResources,\n makeChunkedCursorGuard,\n paginateChunked,\n schemasFromResources,\n selectActivePhases,\n} from '@rawdash/core';\nimport { z } from 'zod';\n\nconst nrqlQuerySchema = z.object({\n name: z\n .string()\n .min(1)\n .regex(/^[a-zA-Z0-9_]+$/, {\n message: 'Metric name must be alphanumeric / underscore',\n }),\n query: z.string().min(1),\n});\n\nexport const configFields = defineConfigFields(\n z.object({\n apiKey: z.object({ $secret: z.string().min(1) }).meta({\n label: 'User API Key',\n description:\n 'New Relic User API key. Create at New Relic -> API keys (User key type, ingest-keys do not work for NerdGraph).',\n placeholder: 'NRAK-...',\n secret: true,\n }),\n accountId: z.number().int().positive().meta({\n label: 'Account ID',\n description:\n 'New Relic account ID the User API key has access to. Find it under New Relic -> Administration -> Access management.',\n placeholder: '1234567',\n }),\n region: z.enum(['US', 'EU']).optional().meta({\n label: 'Region',\n description:\n 'New Relic data region. Defaults to `US` (`api.newrelic.com`); set to `EU` to use `api.eu.newrelic.com`.',\n placeholder: 'US',\n }),\n nrqlQueries: z.array(nrqlQuerySchema).nonempty().optional().meta({\n label: 'NRQL queries (optional)',\n description:\n 'User-declared NRQL queries. Each entry produces `newrelic_nrql_metric` samples named `<name>` from the NerdGraph NRQL API.',\n }),\n resources: z\n .array(z.enum(['alerts', 'alert_violations', 'nrql_queries']))\n .nonempty()\n .optional()\n .meta({\n label: 'Resources',\n description:\n 'Which New Relic resources to sync. Omit to sync all of them.',\n }),\n incidentsLookbackHours: z\n .number()\n .int()\n .positive()\n .max(720)\n .optional()\n .meta({\n label: 'Incidents lookback (hours)',\n description:\n 'Window of NrAiIncident rows to pull on each sync, in hours. Defaults to 168 (7 days). Ignored when `since` is set by the host.',\n placeholder: '168',\n }),\n metricsLookbackHours: z.number().int().positive().max(168).optional().meta({\n label: 'NRQL metrics lookback (hours)',\n description:\n 'Window of NRQL metric samples to pull on each sync, in hours. Defaults to 24. Each user query gets `SINCE <lookback> hours ago` appended unless the query already declares its own `SINCE` clause.',\n placeholder: '24',\n }),\n }),\n);\n\nexport const doc: ConnectorDoc = defineConnectorDoc({\n displayName: 'New Relic',\n category: 'infrastructure',\n brandColor: '#1CE783',\n tagline:\n 'Sync NRQL alert conditions, AI incidents, and user-declared NRQL metric queries from a New Relic account via NerdGraph.',\n vendor: {\n name: 'New Relic',\n domain: 'newrelic.com',\n apiDocs: 'https://docs.newrelic.com/docs/apis/nerdgraph/',\n website: 'https://newrelic.com',\n },\n auth: {\n summary:\n 'A New Relic User API key plus the numeric account ID are required. The key is stored as a secret and used to authenticate every NerdGraph GraphQL request.',\n setup: [\n 'Open New Relic -> API keys and create a `User` key. Ingest-keys are not accepted by NerdGraph.',\n 'Find the numeric account ID under New Relic -> Administration -> Access management. The User key must have access to that account.',\n 'Store the User key as a secret and reference it from the connector config as `apiKey: secret(\"NEWRELIC_USER_KEY\")`.',\n 'Set `accountId` to the numeric account ID, and optionally `region: \"EU\"` if the data lives on the EU host (`api.eu.newrelic.com`); defaults to `US` (`api.newrelic.com`).',\n ],\n },\n rateLimit:\n 'NerdGraph enforces per-account NRQL quotas; this connector retries on 429s through the standard HTTP retry policy.',\n limitations: [\n 'APM-trace deep inspection is out of scope (not dashboard-shaped).',\n 'NRQL queries are single-shot per sync (NRQL does not support cursor pagination); large queries should narrow their `SINCE` window or use `LIMIT MAX`.',\n 'Incidents are pulled via NRQL on the NrAiIncident event type, which depends on Applied Intelligence being enabled on the account.',\n 'Only NRQL-based alert conditions are synced; legacy V1 condition types are not exposed.',\n ],\n});\n\nexport type NewRelicResource = 'alerts' | 'alert_violations' | 'nrql_queries';\n\nexport interface NewRelicNrqlQuery {\n name: string;\n query: string;\n}\n\nexport interface NewRelicSettings {\n accountId: number;\n region?: 'US' | 'EU';\n nrqlQueries?: readonly NewRelicNrqlQuery[];\n resources?: readonly NewRelicResource[];\n incidentsLookbackHours?: number;\n metricsLookbackHours?: number;\n}\n\nconst newRelicCredentials = {\n apiKey: {\n description: 'New Relic User API key',\n auth: 'required' as const,\n },\n} satisfies CredentialsSchema;\n\ntype NewRelicCredentials = typeof newRelicCredentials;\n\nconst PHASE_ORDER = ['alert_conditions', 'incidents', 'metrics'] as const;\n\ntype NewRelicPhase = (typeof PHASE_ORDER)[number];\n\ntype NewRelicSyncCursor = ChunkedSyncCursor<NewRelicPhase, string>;\n\nconst isNewRelicSyncCursor = makeChunkedCursorGuard(PHASE_ORDER);\n\ninterface NrqlConditionNode {\n id: string;\n name: string;\n enabled: boolean;\n policyId: string;\n type: string;\n createdAt: number | null;\n updatedAt: number | null;\n nrql?: { query: string } | null;\n}\n\ninterface NrqlConditionsSearchResult {\n nrqlConditions: NrqlConditionNode[];\n nextCursor: string | null;\n totalCount: number;\n}\n\ninterface NrqlResult {\n results: Array<Record<string, JSONValue>>;\n metadata?: {\n facets?: string[] | null;\n timeWindow?: {\n begin?: number | null;\n end?: number | null;\n } | null;\n } | null;\n}\n\ninterface AlertConditionsResponse {\n actor: {\n account: {\n alerts: {\n nrqlConditionsSearch: NrqlConditionsSearchResult;\n };\n };\n };\n}\n\ninterface NrqlResponse {\n actor: {\n account: {\n nrql: NrqlResult;\n };\n };\n}\n\ninterface MetricsBatchItem {\n queryName: string;\n query: string;\n result: NrqlResult;\n}\n\ninterface GraphQLError {\n message: string;\n path?: Array<string | number>;\n}\n\ninterface GraphQLResponse<T> {\n data?: T;\n errors?: GraphQLError[];\n}\n\nconst ALERT_CONDITIONS_QUERY = `\n query AlertConditions($accountId: Int!, $cursor: String) {\n actor {\n account(id: $accountId) {\n alerts {\n nrqlConditionsSearch(cursor: $cursor) {\n nrqlConditions {\n id\n name\n enabled\n policyId\n type\n createdAt\n updatedAt\n nrql { query }\n }\n nextCursor\n totalCount\n }\n }\n }\n }\n }\n`;\n\nconst NRQL_QUERY = `\n query RunNrql($accountId: Int!, $query: Nrql!) {\n actor {\n account(id: $accountId) {\n nrql(query: $query) {\n results\n metadata {\n facets\n timeWindow {\n begin\n end\n }\n }\n }\n }\n }\n }\n`;\n\nconst idString = z.string().min(1);\nconst nrqlScalarSchema = z.union([\n z.string(),\n z.number(),\n z.boolean(),\n z.null(),\n]);\nconst nrqlCellSchema = z.union([nrqlScalarSchema, z.array(nrqlScalarSchema)]);\n\nconst nrqlConditionNodeSchema = z.object({\n id: idString,\n name: z.string(),\n enabled: z.boolean(),\n policyId: idString,\n type: z.string(),\n createdAt: z.number().nullable(),\n updatedAt: z.number().nullable(),\n nrql: z.object({ query: z.string() }).nullable().optional(),\n});\n\nconst nrqlConditionsSearchResultSchema = z.object({\n nrqlConditions: z.array(nrqlConditionNodeSchema),\n nextCursor: z.string().nullable(),\n totalCount: z.number().int().nonnegative(),\n});\n\nconst nrqlResultSchema = z.object({\n results: z.array(z.record(z.string(), nrqlCellSchema)),\n metadata: z\n .object({\n facets: z.array(z.string()).nullable().optional(),\n timeWindow: z\n .object({\n begin: z.number().nullable().optional(),\n end: z.number().nullable().optional(),\n })\n .nullable()\n .optional(),\n })\n .nullable()\n .optional(),\n});\n\nconst DEFAULT_REGION: 'US' | 'EU' = 'US';\nconst DEFAULT_INCIDENTS_LOOKBACK_HOURS = 168;\nconst DEFAULT_METRICS_LOOKBACK_HOURS = 24;\nconst INCIDENTS_NRQL_LIMIT = 5000;\nconst TIMESTAMP_FIELDS = new Set([\n 'timestamp',\n 'beginTimeSeconds',\n 'endTimeSeconds',\n 'begin_time',\n 'end_time',\n 'beginTime',\n 'endTime',\n 'openedAt',\n 'closedAt',\n]);\nconst FACET_FIELDS = new Set(['facet']);\n\nexport const newRelicResources = defineResources({\n newrelic_alert_condition: {\n shape: 'entity',\n filterable: [],\n description:\n 'NRQL alert conditions with name, enabled state, policy id, type, and the underlying NRQL query string.',\n endpoint:\n 'GraphQL query: actor.account.alerts.nrqlConditionsSearch { nrqlConditions { ... } }',\n responses: { alert_conditions: nrqlConditionsSearchResultSchema },\n },\n newrelic_alert_violation: {\n shape: 'event',\n filterable: [],\n description:\n 'AI alert violation events. Each row from the NrAiIncident event type becomes one event with openedAt / closedAt and the underlying condition / policy metadata.',\n endpoint:\n 'GraphQL nrql() against SELECT ... FROM NrAiIncident WHERE openedAt > ...',\n notes:\n 'Append-only across syncs; the connector filters NrAiIncident by `openedAt` against `options.since` (or the configured lookback) to avoid re-emitting old incidents.',\n responses: { incidents: nrqlResultSchema },\n },\n newrelic_nrql_metric: {\n shape: 'metric',\n dynamic: true,\n description:\n 'User-declared NRQL metric samples, stored as `newrelic_nrql_metric.<query name>`. Each NRQL result row is mapped to a single sample using the first numeric, non-timestamp/facet field as the value.',\n endpoint: 'GraphQL nrql() against the user-declared NRQL query',\n dimensions: [\n { name: 'queryName', description: 'The user-declared query name.' },\n { name: 'query', description: 'The NRQL query string.' },\n {\n name: 'facets',\n description:\n 'Comma-joined facet values for the row, or `*` when the result row is ungrouped.',\n },\n ],\n responses: { nrql_queries: nrqlResultSchema },\n },\n});\n\nexport const id = 'new-relic';\n\nexport class NewRelicConnector extends BaseConnector<\n NewRelicSettings,\n NewRelicCredentials\n> {\n static readonly id = id;\n\n static readonly resources = newRelicResources;\n\n static readonly schemas = schemasFromResources(newRelicResources);\n\n static create(input: unknown, ctx?: ConnectorContext): NewRelicConnector {\n const parsed = configFields.parse(input);\n return new NewRelicConnector(\n {\n accountId: parsed.accountId,\n region: parsed.region,\n nrqlQueries: parsed.nrqlQueries,\n resources: parsed.resources,\n incidentsLookbackHours: parsed.incidentsLookbackHours,\n metricsLookbackHours: parsed.metricsLookbackHours,\n },\n { apiKey: parsed.apiKey },\n ctx,\n );\n }\n\n readonly id = id;\n override readonly credentials = newRelicCredentials;\n\n private get endpoint(): string {\n const region = this.settings.region ?? DEFAULT_REGION;\n return region === 'EU'\n ? 'https://api.eu.newrelic.com/graphql'\n : 'https://api.newrelic.com/graphql';\n }\n\n private buildHeaders(): Record<string, string> {\n return {\n 'API-Key': this.creds.apiKey,\n 'Content-Type': 'application/json',\n 'User-Agent': connectorUserAgent('new-relic'),\n };\n }\n\n private async graphql<T>(\n query: string,\n variables: Record<string, unknown>,\n resource: string,\n signal?: AbortSignal,\n ): Promise<HttpResponse<GraphQLResponse<T>>> {\n const res = await this.post<GraphQLResponse<T>>(this.endpoint, {\n resource,\n headers: this.buildHeaders(),\n body: JSON.stringify({ query, variables }),\n signal,\n });\n if (res.body.errors && res.body.errors.length > 0) {\n const messages = res.body.errors.map((e) => e.message).join('; ');\n throw new Error(`New Relic NerdGraph error: ${messages}`);\n }\n if (!res.body.data) {\n throw new Error(\n `New Relic NerdGraph response missing data for resource '${resource}'`,\n );\n }\n return res;\n }\n\n private activePhases(): NewRelicPhase[] {\n return selectActivePhases<NewRelicResource, NewRelicPhase>(\n (r) => {\n switch (r) {\n case 'alerts':\n return 'alert_conditions';\n case 'alert_violations':\n return 'incidents';\n case 'nrql_queries':\n return 'metrics';\n }\n },\n PHASE_ORDER,\n this.settings.resources,\n );\n }\n\n private async fetchAlertConditionsPage(\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<{ items: NrqlConditionNode[]; next: string | null }> {\n const res = await this.graphql<AlertConditionsResponse>(\n ALERT_CONDITIONS_QUERY,\n { accountId: this.settings.accountId, cursor: page },\n 'alert_conditions',\n signal,\n );\n const search = res.body.data!.actor.account.alerts.nrqlConditionsSearch;\n return {\n items: search.nrqlConditions,\n next: search.nextCursor,\n };\n }\n\n private async fetchIncidents(\n options: SyncOptions,\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<{ items: Array<Record<string, JSONValue>>; next: string | null }> {\n const sinceMs = options.since ? parseEpoch(options.since, 'iso') : null;\n const lookbackHours =\n this.settings.incidentsLookbackHours ?? DEFAULT_INCIDENTS_LOOKBACK_HOURS;\n const fromMs = sinceMs ?? Date.now() - lookbackHours * 60 * 60 * 1000;\n const floorMs = page !== null ? Number(page) : fromMs;\n const nrql = `SELECT incidentId, conditionFamilyId, policyName, conditionName, openedAt, closedAt, durationSeconds, priority, title, state, entityGuid FROM NrAiIncident WHERE openedAt > ${floorMs} ORDER BY openedAt ASC LIMIT ${INCIDENTS_NRQL_LIMIT}`;\n const res = await this.graphql<NrqlResponse>(\n NRQL_QUERY,\n { accountId: this.settings.accountId, query: nrql },\n 'incidents',\n signal,\n );\n const results = res.body.data!.actor.account.nrql.results;\n if (results.length < INCIDENTS_NRQL_LIMIT) {\n return { items: results, next: null };\n }\n const lastOpenedAt = results[results.length - 1]?.openedAt;\n const next =\n typeof lastOpenedAt === 'number' &&\n Number.isFinite(lastOpenedAt) &&\n lastOpenedAt > floorMs\n ? String(lastOpenedAt)\n : null;\n return { items: results, next };\n }\n\n private async fetchMetrics(\n options: SyncOptions,\n signal: AbortSignal | undefined,\n ): Promise<{ items: MetricsBatchItem[]; next: string | null }> {\n const queries = this.settings.nrqlQueries ?? [];\n if (queries.length === 0) {\n return { items: [], next: null };\n }\n const lookbackHours =\n this.settings.metricsLookbackHours ?? DEFAULT_METRICS_LOOKBACK_HOURS;\n const items: MetricsBatchItem[] = [];\n for (const q of queries) {\n signal?.throwIfAborted();\n const nrql = this.appendSinceIfMissing(\n q.query,\n options.since ?? null,\n lookbackHours,\n );\n const res = await this.graphql<NrqlResponse>(\n NRQL_QUERY,\n { accountId: this.settings.accountId, query: nrql },\n 'nrql_queries',\n signal,\n );\n items.push({\n queryName: q.name,\n query: q.query,\n result: res.body.data!.actor.account.nrql,\n });\n }\n return { items, next: null };\n }\n\n private appendSinceIfMissing(\n query: string,\n since: string | null,\n fallbackHours: number,\n ): string {\n if (/\\bSINCE\\b/i.test(query)) {\n return query;\n }\n const sinceMs = since ? parseEpoch(since, 'iso') : null;\n if (sinceMs !== null) {\n return `${query} SINCE ${sinceMs}`;\n }\n return `${query} SINCE ${fallbackHours} hours ago`;\n }\n\n private async writeAlertConditions(\n storage: StorageHandle,\n conditions: NrqlConditionNode[],\n ): Promise<void> {\n for (const c of conditions) {\n const createdMs = c.createdAt ?? null;\n const modifiedMs = c.updatedAt ?? null;\n const updatedAt = modifiedMs ?? createdMs ?? Date.now();\n await storage.entity({\n type: 'newrelic_alert_condition',\n id: c.id,\n attributes: {\n conditionId: c.id,\n name: c.name,\n enabled: c.enabled,\n policyId: c.policyId,\n conditionType: c.type,\n nrqlQuery: c.nrql?.query ?? null,\n createdAt: createdMs,\n modifiedAt: modifiedMs,\n },\n updated_at: updatedAt,\n });\n }\n }\n\n private async writeIncidents(\n storage: StorageHandle,\n rows: Array<Record<string, JSONValue>>,\n ): Promise<void> {\n for (const row of rows) {\n const incidentId =\n typeof row.incidentId === 'string' || typeof row.incidentId === 'number'\n ? String(row.incidentId)\n : null;\n const openedAtRaw = row.openedAt;\n const openedAtMs =\n typeof openedAtRaw === 'number' && Number.isFinite(openedAtRaw)\n ? openedAtRaw\n : null;\n if (incidentId === null || openedAtMs === null) {\n continue;\n }\n const closedAtRaw = row.closedAt;\n const closedAtMs =\n typeof closedAtRaw === 'number' && Number.isFinite(closedAtRaw)\n ? closedAtRaw\n : null;\n await storage.event({\n name: 'newrelic_alert_violation',\n start_ts: openedAtMs,\n end_ts: closedAtMs,\n attributes: {\n incidentId,\n conditionFamilyId: this.coerceScalar(row.conditionFamilyId),\n conditionName: this.coerceScalar(row.conditionName),\n policyName: this.coerceScalar(row.policyName),\n priority: this.coerceScalar(row.priority),\n title: this.coerceScalar(row.title),\n state: this.coerceScalar(row.state),\n entityGuid: this.coerceScalar(row.entityGuid),\n durationSeconds: this.coerceScalar(row.durationSeconds),\n },\n });\n }\n }\n\n private coerceScalar(value: JSONValue | undefined): JSONValue {\n if (value === undefined) {\n return null;\n }\n return value;\n }\n\n private async writeMetrics(\n storage: StorageHandle,\n items: MetricsBatchItem[],\n ): Promise<void> {\n if (items.length === 0) {\n return;\n }\n const samplesByName: Map<\n string,\n Array<{\n name: string;\n ts: number;\n value: number;\n attributes: Record<string, string | number>;\n }>\n > = new Map();\n for (const item of items) {\n const facets = item.result.metadata?.facets ?? [];\n for (const row of item.result.results) {\n const ts = this.extractTimestamp(row, item.result);\n if (ts === null) {\n continue;\n }\n const value = this.extractValue(row);\n if (value === null) {\n continue;\n }\n const facetsStr = this.extractFacets(row, facets ?? []);\n const name = `newrelic_nrql_metric.${item.queryName}`;\n let bucket = samplesByName.get(name);\n if (!bucket) {\n bucket = [];\n samplesByName.set(name, bucket);\n }\n bucket.push({\n name,\n ts,\n value,\n attributes: {\n queryName: item.queryName,\n query: item.query,\n facets: facetsStr,\n },\n });\n }\n }\n for (const [name, samples] of samplesByName) {\n await storage.metrics(samples, { names: [name] });\n }\n }\n\n private extractTimestamp(\n row: Record<string, JSONValue>,\n result: NrqlResult,\n ): number | null {\n const endTimeSeconds = row.endTimeSeconds;\n if (typeof endTimeSeconds === 'number' && Number.isFinite(endTimeSeconds)) {\n return parseEpoch(endTimeSeconds, 's');\n }\n const beginTimeSeconds = row.beginTimeSeconds;\n if (\n typeof beginTimeSeconds === 'number' &&\n Number.isFinite(beginTimeSeconds)\n ) {\n return parseEpoch(beginTimeSeconds, 's');\n }\n const timestamp = row.timestamp;\n if (typeof timestamp === 'number' && Number.isFinite(timestamp)) {\n return parseEpoch(timestamp, 'ms');\n }\n const endMs = result.metadata?.timeWindow?.end;\n if (typeof endMs === 'number' && Number.isFinite(endMs)) {\n return parseEpoch(endMs, 'ms');\n }\n return Date.now();\n }\n\n private extractValue(row: Record<string, JSONValue>): number | null {\n for (const [key, value] of Object.entries(row)) {\n if (TIMESTAMP_FIELDS.has(key) || FACET_FIELDS.has(key)) {\n continue;\n }\n if (typeof value === 'number' && Number.isFinite(value)) {\n return value;\n }\n }\n return null;\n }\n\n private extractFacets(\n row: Record<string, JSONValue>,\n facetNames: string[],\n ): string {\n const facetRaw = row.facet;\n if (Array.isArray(facetRaw)) {\n return facetRaw.length > 0 ? facetRaw.map(String).join(',') : '*';\n }\n if (typeof facetRaw === 'string' && facetRaw.length > 0) {\n return facetRaw;\n }\n if (facetNames.length > 0) {\n const parts: string[] = [];\n for (const fname of facetNames) {\n const v = row[fname];\n if (v !== undefined && v !== null) {\n parts.push(String(v));\n }\n }\n if (parts.length > 0) {\n return parts.join(',');\n }\n }\n return '*';\n }\n\n async sync(\n options: SyncOptions,\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<SyncResult> {\n const cursor: NewRelicSyncCursor | undefined = isNewRelicSyncCursor(\n options.cursor,\n )\n ? options.cursor\n : undefined;\n const isFull = options.mode === 'full';\n const phases = this.activePhases();\n\n return paginateChunked<NewRelicPhase, string>({\n phases,\n cursor,\n signal,\n logger: this.logger,\n fetchPage: async (phase, page, sig) => {\n switch (phase) {\n case 'alert_conditions':\n return this.fetchAlertConditionsPage(page, sig);\n case 'incidents':\n return this.fetchIncidents(options, page, sig);\n case 'metrics':\n return this.fetchMetrics(options, sig);\n }\n },\n writeBatch: async (phase, items, page) => {\n if (isFull && page === null) {\n switch (phase) {\n case 'alert_conditions':\n await storage.entities([], {\n types: ['newrelic_alert_condition'],\n });\n break;\n case 'incidents':\n await storage.events([], { names: ['newrelic_alert_violation'] });\n break;\n case 'metrics':\n for (const q of this.settings.nrqlQueries ?? []) {\n await storage.metrics([], {\n names: [`newrelic_nrql_metric.${q.name}`],\n });\n }\n break;\n }\n }\n switch (phase) {\n case 'alert_conditions':\n return this.writeAlertConditions(\n storage,\n items as NrqlConditionNode[],\n );\n case 'incidents':\n return this.writeIncidents(\n storage,\n items as Array<Record<string, JSONValue>>,\n );\n case 'metrics':\n return this.writeMetrics(storage, items as MetricsBatchItem[]);\n }\n },\n });\n }\n}\n","import { NewRelicConnector } from './new-relic';\n\nexport {\n configFields,\n doc,\n NewRelicConnector,\n id,\n newRelicResources as resources,\n} from './new-relic';\nexport type { NewRelicResource, NewRelicSettings } from './new-relic';\nexport default NewRelicConnector;\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rawdash/connector-new-relic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "Rawdash connector for New Relic — NRQL alert conditions, incidents, and user-declared NRQL metric queries via NerdGraph.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -24,15 +24,15 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"zod": "^4.4.3",
|
|
27
|
-
"@rawdash/core": "0.
|
|
27
|
+
"@rawdash/core": "0.24.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"fast-check": "^4.8.0",
|
|
31
31
|
"tsup": "^8.0.0",
|
|
32
32
|
"typescript": "^5.7.2",
|
|
33
33
|
"vitest": "^4.1.4",
|
|
34
|
-
"@rawdash/connector-
|
|
35
|
-
"@rawdash/connector-
|
|
34
|
+
"@rawdash/connector-shared": "0.3.1",
|
|
35
|
+
"@rawdash/connector-test-utils": "0.0.10"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "tsup",
|