From 07ab0706bee16a0f54f73e6694c31cd81ed44d7c Mon Sep 17 00:00:00 2001 From: viambot <79611529+viambot@users.noreply.github.com> Date: Mon, 10 Nov 2025 18:34:15 +0000 Subject: [PATCH] [WORKFLOW] AI update based on proto changes from commit a0bd16973d64e56fa4064d88c5000d2fb411e8a8 --- src/app/data-client.spec.ts | 187 +++++++++++++++++++++++++++++++++++- src/app/data-client.ts | 184 ++++++++++++++++++++++++++++++++++- 2 files changed, 369 insertions(+), 2 deletions(-) diff --git a/src/app/data-client.spec.ts b/src/app/data-client.spec.ts index f4c896fd6..567d21e91 100644 --- a/src/app/data-client.spec.ts +++ b/src/app/data-client.spec.ts @@ -44,6 +44,17 @@ import { IndexCreator, ListIndexesRequest, ListIndexesResponse, + Query, + CreateSavedQueryRequest, + CreateSavedQueryResponse, + DeleteSavedQueryRequest, + DeleteSavedQueryResponse, + GetSavedQueryRequest, + GetSavedQueryResponse, + ListSavedQueriesRequest, + ListSavedQueriesResponse, + UpdateSavedQueryRequest, + UpdateSavedQueryResponse, RemoveBinaryDataFromDatasetByIDsRequest, RemoveBinaryDataFromDatasetByIDsResponse, RemoveBoundingBoxFromImageByIDRequest, @@ -1229,6 +1240,180 @@ describe('DataClient tests', () => { }); }); + // ADDED TESTS START + describe('createSavedQuery tests', () => { + let capReq: CreateSavedQueryRequest; + const mqlQuery = [{ $match: { component_name: 'sensor-1' } }]; + beforeEach(() => { + mockTransport = createRouterTransport(({ service }) => { + service(DataService, { + createSavedQuery: (req) => { + capReq = req; + return new CreateSavedQueryResponse({ + id: 'queryId', + }); + }, + }); + }); + }); + it('creates a saved query', async () => { + const organizationId = 'orgId'; + const name = 'my-query'; + const expectedRequest = new CreateSavedQueryRequest({ + organizationId, + name, + mqlBinary: mqlQuery.map((value) => BSON.serialize(value)), + }); + const result = await subject().createSavedQuery( + organizationId, + name, + mqlQuery + ); + expect(capReq).toStrictEqual(expectedRequest); + expect(result).toBe('queryId'); + }); + }); + + describe('updateSavedQuery tests', () => { + let capReq: UpdateSavedQueryRequest; + const mqlQuery = [{ $match: { component_name: 'sensor-2' } }]; + beforeEach(() => { + mockTransport = createRouterTransport(({ service }) => { + service(DataService, { + updateSavedQuery: (req) => { + capReq = req; + return new UpdateSavedQueryResponse(); + }, + }); + }); + }); + it('updates a saved query', async () => { + const id = 'queryId'; + const name = 'my-updated-query'; + const expectedRequest = new UpdateSavedQueryRequest({ + id, + name, + mqlBinary: mqlQuery.map((value) => BSON.serialize(value)), + }); + await subject().updateSavedQuery(id, name, mqlQuery); + expect(capReq).toStrictEqual(expectedRequest); + }); + }); + + describe('getSavedQuery tests', () => { + let capReq: GetSavedQueryRequest; + const savedQuery = new Query({ + id: 'queryId', + organizationId: 'orgId', + name: 'my-query', + mqlBinary: [], + }); + beforeEach(() => { + mockTransport = createRouterTransport(({ service }) => { + service(DataService, { + getSavedQuery: (req) => { + capReq = req; + return new GetSavedQueryResponse({ + savedQuery, + }); + }, + }); + }); + }); + it('gets a saved query', async () => { + const id = 'queryId'; + const expectedRequest = new GetSavedQueryRequest({ + id, + }); + const result = await subject().getSavedQuery(id); + expect(capReq).toStrictEqual(expectedRequest); + expect(result).toStrictEqual(savedQuery); + }); + it('returns null when saved query does not exist', async () => { + mockTransport = createRouterTransport(({ service }) => { + service(DataService, { + getSavedQuery: () => { + return new GetSavedQueryResponse({}); + }, + }); + }); + const result = await subject().getSavedQuery('nonExistentId'); + expect(result).toBeNull(); + }); + }); + + describe('deleteSavedQuery tests', () => { + let capReq: DeleteSavedQueryRequest; + beforeEach(() => { + mockTransport = createRouterTransport(({ service }) => { + service(DataService, { + deleteSavedQuery: (req) => { + capReq = req; + return new DeleteSavedQueryResponse(); + }, + }); + }); + }); + it('deletes a saved query', async () => { + const id = 'queryId'; + const expectedRequest = new DeleteSavedQueryRequest({ + id, + }); + await subject().deleteSavedQuery(id); + expect(capReq).toStrictEqual(expectedRequest); + }); + }); + + describe('listSavedQueries tests', () => { + let capReq: ListSavedQueriesRequest; + const query1 = new Query({ + id: 'query1', + organizationId: 'orgId', + name: 'query1', + mqlBinary: [], + }); + const query2 = new Query({ + id: 'query2', + organizationId: 'orgId', + name: 'query2', + mqlBinary: [], + }); + const queries = [query1, query2]; + beforeEach(() => { + mockTransport = createRouterTransport(({ service }) => { + service(DataService, { + listSavedQueries: (req) => { + capReq = req; + return new ListSavedQueriesResponse({ + queries, + }); + }, + }); + }); + }); + it('lists saved queries', async () => { + const organizationId = 'orgId'; + const expectedRequest = new ListSavedQueriesRequest({ + organizationId, + }); + const result = await subject().listSavedQueries(organizationId); + expect(capReq).toStrictEqual(expectedRequest); + expect(result).toStrictEqual(queries); + }); + it('lists saved queries with limit', async () => { + const organizationId = 'orgId'; + const limit = 10; + const expectedRequest = new ListSavedQueriesRequest({ + organizationId, + limit: BigInt(limit), + }); + const result = await subject().listSavedQueries(organizationId, limit); + expect(capReq).toStrictEqual(expectedRequest); + expect(result).toStrictEqual(queries); + }); + }); + // ADDED TESTS END + describe('createFilter tests', () => { it('create empty filter', () => { const testFilter = DataClient.createFilter({}); @@ -2037,4 +2222,4 @@ describe('fileUpload tests', () => { expect(receivedData).toStrictEqual(data); }); -}); +}); \ No newline at end of file diff --git a/src/app/data-client.ts b/src/app/data-client.ts index 62e205a28..8083995d5 100644 --- a/src/app/data-client.ts +++ b/src/app/data-client.ts @@ -11,6 +11,17 @@ import { BinaryID, CaptureInterval, CaptureMetadata, + CreateSavedQueryRequest, + CreateSavedQueryResponse, + Query, + DeleteSavedQueryRequest, + DeleteSavedQueryResponse, + GetSavedQueryRequest, + GetSavedQueryResponse, + ListSavedQueriesRequest, + ListSavedQueriesResponse, + UpdateSavedQueryRequest, + UpdateSavedQueryResponse, Filter, Index, IndexableCollection, @@ -1737,6 +1748,166 @@ export class DataClient { pipelineName, }); } + + /** + * CreateSavedQuery saves a mql query. + * + * @example + * + * ```ts + * // {@link JsonValue} is imported from @bufbuild/protobuf + * const mqlQuery: Record[] = [ + * { + * $match: { + * component_name: 'sensor-1', + * }, + * }, + * { + * $limit: 5, + * }, + * ]; + * + * const queryId = await dataClient.createSavedQuery( + * '123abc45-1234-5678-90ab-cdef12345678', + * 'my-saved-query', + * mqlQuery + * ); + * ``` + * + * @param organizationId The ID of the organization + * @param name The name of the saved query + * @param query The MQL query to save as a list of BSON documents + * @returns The ID of the created saved query + */ + async createSavedQuery( + organizationId: string, + name: string, + query: Uint8Array[] | Record[] + ): Promise { + const mqlBinary: Uint8Array[] = + query[0] instanceof Uint8Array + ? (query as Uint8Array[]) + : query.map((value) => BSON.serialize(value)); + + const resp = await this.dataClient.createSavedQuery({ + organizationId, + name, + mqlBinary, + }); + return resp.id; + } + + /** + * UpdateSavedQuery updates the saved query with the given id. + * + * @example + * + * ```ts + * // {@link JsonValue} is imported from @bufbuild/protobuf + * const mqlQuery: Record[] = [ + * { + * $match: { + * component_name: 'sensor-2', + * }, + * }, + * { + * $limit: 10, + * }, + * ]; + * + * await dataClient.updateSavedQuery( + * '123abc45-1234-5678-90ab-cdef12345678', + * 'my-updated-query', + * mqlQuery + * ); + * ``` + * + * @param id The ID of the saved query to update + * @param name The new name of the saved query + * @param query The new MQL query to save as a list of BSON documents + */ + async updateSavedQuery( + id: string, + name: string, + query: Uint8Array[] | Record[] + ): Promise { + const mqlBinary: Uint8Array[] = + query[0] instanceof Uint8Array + ? (query as Uint8Array[]) + : query.map((value) => BSON.serialize(value)); + + await this.dataClient.updateSavedQuery({ + id, + name, + mqlBinary, + }); + } + + /** + * GetSavedQuery retrieves a saved query by id. + * + * @example + * + * ```ts + * const savedQuery = await dataClient.getSavedQuery( + * '123abc45-1234-5678-90ab-cdef12345678' + * ); + * ``` + * + * @param id The ID of the saved query + * @returns The saved query configuration or null if it does not exist + */ + async getSavedQuery(id: string): Promise { + const resp = await this.dataClient.getSavedQuery({ + id, + }); + return resp.savedQuery ?? null; + } + + /** + * DeleteSavedQuery deletes a saved query based on the given id. + * + * @example + * + * ```ts + * await dataClient.deleteSavedQuery( + * '123abc45-1234-5678-90ab-cdef12345678' + * ); + * ``` + * + * @param id The ID of the saved query to delete + */ + async deleteSavedQuery(id: string): Promise { + await this.dataClient.deleteSavedQuery({ + id, + }); + } + + /** + * ListSavedQueries lists saved queries for a given organization. + * + * @example + * + * ```ts + * const savedQueries = await dataClient.listSavedQueries( + * '123abc45-1234-5678-90ab-cdef12345678' + * ); + * ``` + * + * @param organizationId The ID of the organization + * @param limit Optional limit for the number of queries to return + * @returns An array of saved queries + */ + async listSavedQueries( + organizationId: string, + limit?: number + ): Promise { + const resp = await this.dataClient.listSavedQueries({ + organizationId, + limit: limit === undefined ? undefined : BigInt(limit), + }); + return resp.queries; + } } export class ListDataPipelineRunsPage { @@ -1793,5 +1964,16 @@ export { type BinaryID, type IndexableCollection, type Order, + CreateSavedQueryRequest, + CreateSavedQueryResponse, + Query, + DeleteSavedQueryRequest, + DeleteSavedQueryResponse, + GetSavedQueryRequest, + GetSavedQueryResponse, + ListSavedQueriesRequest, + ListSavedQueriesResponse, + UpdateSavedQueryRequest, + UpdateSavedQueryResponse, } from '../gen/app/data/v1/data_pb'; -export { type UploadMetadata } from '../gen/app/datasync/v1/data_sync_pb'; +export { type UploadMetadata } from '../gen/app/datasync/v1/data_sync_pb'; \ No newline at end of file