UNPKG

@axiomhq/js

Version:

The official javascript bindings for the Axiom API

288 lines (285 loc) 10.5 kB
import { datasets } from './datasets.js'; import { users } from './users.js'; import { createBatchKey, Batch } from './batch.js'; import HTTPClient from './httpClient.js'; import { isAxiomPersonalToken } from './token.js'; class BaseClient extends HTTPClient { datasets; users; localPath = '/v1'; onError = console.error; constructor(options) { if (options.token && isAxiomPersonalToken(options.token)) { console.warn('Using a personal token (`xapt-...`) is deprecated for security reasons. Please use an API token (`xaat-...`) instead. Support for personal tokens will be removed in a future release.'); } super(options); this.datasets = new datasets.Service(options); this.users = new users.Service(options); if (options.onError) { this.onError = options.onError; } } /** * Ingest events into the provided dataset using raw data types, e.g: string, buffer or a stream. * * @param dataset - name of the dataset to ingest events into * @param data - data to be ingested * @param contentType - optional content type, defaults to JSON * @param contentEncoding - optional content encoding, defaults to Identity * @param options - optional ingest options * @returns result a promise of ingest and its status, check: {@link IngestStatus} * * @example * ``` * import { AxiomWithoutBatching } from '@axiomhq/js'; * * const axiom = new AxiomWithoutBatching(); * ``` * */ ingestRaw = async (dataset, data, contentType = ContentType.JSON, contentEncoding = ContentEncoding.Identity, options) => { try { return await this.client.post(this.localPath + '/datasets/' + dataset + '/ingest', { headers: { 'Content-Type': contentType, 'Content-Encoding': contentEncoding, }, body: data, }, { 'timestamp-field': options?.timestampField, 'timestamp-format': options?.timestampFormat, 'csv-delimiter': options?.csvDelimiter, }); } catch (err) { this.onError(err); return await Promise.resolve({ ingested: 0, failed: 0, processedBytes: 0, blocksCreated: 0, walLength: 0, }); } }; queryLegacy = (dataset, query, options) => this.client.post(this.localPath + '/datasets/' + dataset + '/query', { body: JSON.stringify(query), }, { 'streaming-duration': options?.streamingDuration, nocache: options?.noCache, }, 120_000); /** * Executes APL query using the provided APL and returns the result * * @param apl - the apl query * @param options - optional query options * @returns result of the query depending on the format in options, check: {@link QueryResult} and {@link TabularQueryResult} * * @example * ``` * await axiom.query("['dataset'] | count"); * ``` * */ query = (apl, options) => { const req = { apl: apl }; if (options?.startTime) { req.startTime = options?.startTime; } if (options?.endTime) { req.endTime = options?.endTime; } return this.client .post(this.localPath + '/datasets/_apl', { body: JSON.stringify(req), }, { 'streaming-duration': options?.streamingDuration, nocache: options?.noCache, format: options?.format ?? 'legacy', }, 120_000) .then((res) => { if (options?.format !== 'tabular') { return res; } const result = res; return { ...res, tables: result.tables.map((t) => { return { ...t, events: function* () { let iteration = 0; if (!this.columns) { return; } while (iteration <= this.columns[0].length) { const value = Object.fromEntries(this.fields.map((field, fieldIdx) => [field.name, this.columns[fieldIdx][iteration]])); if (iteration >= this.columns[0].length) { return value; } yield value; iteration++; } }, }; }), }; }); }; /** * Executes APL query using the provided APL and returns the result. * This is just an alias for the `query()` method, please use that instead. * * @param apl - the apl query * @param options - optional query options * @returns Promise<QueryResult> * * @example * ``` * await axiom.aplQuery("['dataset'] | count"); * ``` */ aplQuery = (apl, options) => this.query(apl, options); } /** * Axiom's client without batching events in the background. * In most cases you'll want to use the {@link Axiom} client instead. * * * @param options - The {@link ClientOptions} to configure authentication * */ class AxiomWithoutBatching extends BaseClient { /** * Ingest event(s) asynchronously * * @param dataset - name of the dataset to ingest events into * @param events - list of events to be ingested, could be a single object as well * @param options - optional ingest options * @returns the result of the ingest, check: {@link IngestStatus} * * @example * ``` * import { AxiomWithoutBatching } from '@axiomhq/js'; * * const axiom = new AxiomWithoutBatching(); * await axiom.ingest('dataset-name', [{ foo: 'bar' }]) * ``` * */ async ingest(dataset, events, options) { const array = Array.isArray(events) ? events : [events]; const json = array.map((v) => JSON.stringify(v)).join('\n'); return this.ingestRaw(dataset, json, ContentType.NDJSON, ContentEncoding.Identity, options); } } /** * Axiom's default client that queues events in the background, * sends them asynchronously to the server every 1s or every 1000 events. * * @param options - The options passed to the client * */ class Axiom extends BaseClient { batch = {}; /** * Ingest events asynchronously * * @remarks * Events passed to ingest method will be queued in a batch and sent * in the background every second or every 1000 events. * * @param dataset - name of the dataset to ingest events into * @param events - list of events to be ingested, could be a single object as well * @param options - optional ingest options * @returns void, as the events are sent in the background * */ ingest = (dataset, events, options) => { const key = createBatchKey(dataset, options); if (!this.batch[key]) { this.batch[key] = new Batch((dataset, events, options) => { const array = Array.isArray(events) ? events : [events]; const json = array.map((v) => JSON.stringify(v)).join('\n'); return this.ingestRaw(dataset, json, ContentType.NDJSON, ContentEncoding.Identity, options); }, dataset, options); } return this.batch[key].ingest(events); }; /** * Flushes all the events that have been queued in the background * * @remarks * calling `await flush()` will wait for all the events to be sent to the server * and is necessary to ensure data delivery. */ flush = async () => { let promises = []; for (const key in this.batch) { promises.push(this.batch[key].flush().catch(this.onError)); } await Promise.all(promises).catch(this.onError); }; } BigInt.prototype.toJSON = function () { return this.toString(); }; var ContentType; (function (ContentType) { ContentType["JSON"] = "application/json"; ContentType["NDJSON"] = "application/x-ndjson"; ContentType["CSV"] = "text/csv"; })(ContentType || (ContentType = {})); var ContentEncoding; (function (ContentEncoding) { ContentEncoding["Identity"] = ""; ContentEncoding["GZIP"] = "gzip"; })(ContentEncoding || (ContentEncoding = {})); var AggregationOp; (function (AggregationOp) { AggregationOp["Count"] = "count"; AggregationOp["Distinct"] = "distinct"; AggregationOp["Sum"] = "sum"; AggregationOp["Avg"] = "avg"; AggregationOp["Min"] = "min"; AggregationOp["Max"] = "max"; AggregationOp["Topk"] = "topk"; AggregationOp["Percentiles"] = "percentiles"; AggregationOp["Histogram"] = "histogram"; AggregationOp["Variance"] = "variance"; AggregationOp["Stdev"] = "stdev"; AggregationOp["ArgMin"] = "argmin"; AggregationOp["ArgMax"] = "argmax"; AggregationOp["MakeSet"] = "makeset"; AggregationOp["MakeSetIf"] = "makesetif"; AggregationOp["CountIf"] = "countif"; AggregationOp["CountDistinctIf"] = "distinctif"; })(AggregationOp || (AggregationOp = {})); var FilterOp; (function (FilterOp) { FilterOp["And"] = "and"; FilterOp["Or"] = "or"; FilterOp["Not"] = "not"; FilterOp["Equal"] = "=="; FilterOp["NotEqual"] = "!="; FilterOp["Exists"] = "exists"; FilterOp["NotExists"] = "not-exists"; FilterOp["GreaterThan"] = ">"; FilterOp["GreaterThanOrEqualTo"] = ">="; FilterOp["LessThan"] = "<"; FilterOp["LessThanOrEqualTo"] = "<="; FilterOp["Gt"] = "gt"; FilterOp["Gte"] = "gte"; FilterOp["Lt"] = "lt"; FilterOp["Lte"] = "lte"; FilterOp["StartsWith"] = "starts-with"; FilterOp["NotStartsWith"] = "not-starts-with"; FilterOp["EndsWith"] = "ends-with"; FilterOp["NotEndsWith"] = "not-ends-with"; FilterOp["Contains"] = "contains"; FilterOp["NotContains"] = "not-contains"; FilterOp["Regexp"] = "regexp"; FilterOp["NotRegexp"] = "not-regexp"; })(FilterOp || (FilterOp = {})); export { AggregationOp, Axiom, AxiomWithoutBatching, ContentEncoding, ContentType, FilterOp }; //# sourceMappingURL=client.js.map