UNPKG

@jupiterone/jupiterone-mcp

Version:

Model Context Protocol server for JupiterOne account rules and rule details

90 lines 3.85 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.J1qlService = exports.J1qlQueryTimeoutError = void 0; const queries_js_1 = require("../graphql/queries.js"); const appUrl_js_1 = require("../../utils/appUrl.js"); /** * Thrown when a deferred query is still running after the poll budget is exhausted. A distinct type * lets the widget pre-create validation gate treat a timeout as advisory (proceed) while a real * query error still blocks — without changing execute-j1ql-query's own timeout handling. */ class J1qlQueryTimeoutError extends Error { constructor(seconds) { super(`Query timed out after ${seconds} seconds`); this.name = 'J1qlQueryTimeoutError'; } } exports.J1qlQueryTimeoutError = J1qlQueryTimeoutError; class J1qlService { client; abortSignal; constructor(client, abortSignal) { this.client = client; this.abortSignal = abortSignal; } /** * Construct query results URL based on subdomain */ constructQueryUrl(query, subdomain) { const params = new URLSearchParams({ q: query }); return (0, appUrl_js_1.constructAppUrl)(`home?${params.toString()}`, subdomain); } /** * Execute a J1QL query */ async executeJ1qlQuery({ query, variables, cursor, scopeFilters, flags, maxPollAttempts, }) { // Use queryV2 for better error messages const response = await this.client.request(queries_js_1.QUERY_V2, { query, variables, cursor, scopeFilters, includeDeleted: flags?.includeDeleted, returnRowMetadata: flags?.returnRowMeta, returnComputedProperties: flags?.computedProperties, }); // Handle deferred response if (response.queryV2.type === 'deferred' && response.queryV2.url) { // Poll the URL until results are ready const maxAttempts = maxPollAttempts ?? 60; // 60 attempts = 1 minute max wait const pollInterval = 1000; // 1 second between polls for (let attempt = 0; attempt < maxAttempts; attempt++) { // Honor the host's abort signal: it fires when the caller disconnects or a deadline passes. this.abortSignal?.throwIfAborted(); const fetchResponse = await fetch(response.queryV2.url, { signal: this.abortSignal }); if (!fetchResponse.ok) { throw new Error(`Failed to fetch query results: ${fetchResponse.statusText}`); } const result = await fetchResponse.json(); // Check if the query is complete if (result.status === 'COMPLETE' || result.status === 'FAILED') { if (result.status === 'FAILED') { throw new Error(result.error || 'Query execution failed'); } return result; } // If still in progress, wait before next poll if (result.status === 'IN_PROGRESS') { await new Promise(resolve => setTimeout(resolve, pollInterval)); continue; } // If we get an unexpected status, return the result as-is return result; } throw new J1qlQueryTimeoutError(maxAttempts); } return response.queryV2; } /** * List all entity types in the account */ async listEntityTypes() { const response = await this.client.request(queries_js_1.GET_ENTITY_COUNTS); return { classes: response.getEntityCounts?.classes ?? {}, types: response.getEntityCounts?.types ?? {}, }; } } exports.J1qlService = J1qlService; //# sourceMappingURL=j1ql-service.js.map