Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 78 additions & 1 deletion src/models/data-fabric/entities.models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import {
EntityUpdateRecordsOptions,
EntityDeleteRecordsOptions,
EntityGetAllRecordsOptions,
EntityInsertRecordOptions
EntityInsertRecordOptions,
EntityQueryOptions,
EntityQueryResponse
} from './entities.types';
import { PaginatedResponse, NonPaginatedResponse, HasPaginationOptions } from '../../utils/pagination/types';

Expand Down Expand Up @@ -461,6 +463,60 @@ export interface EntityServiceModel {
* ```
*/
deleteAttachment(entityId: string, recordId: string, fieldName: string): Promise<EntityDeleteAttachmentResponse>;

/**
*
* @experimental
*
* /// warning
Preview: This method is experimental and may change or be removed in future releases.
///
*
* Queries an entity by ID with support for joins, aggregates, filters, grouping, and sorting
*
* @param entityId - UUID of the entity to query
* @param options - Query options including selectedFields, aggregates, joins, filterGroup, groupBy, sortOptions, start, limit
* @returns Promise resolving to query response with matching records
* {@link EntityQueryResponse}
* @example
* ```typescript
* import { Entities } from '@uipath/uipath-typescript/entities';
*
* const entities = new Entities(sdk);
*
* // Simple query with filters
* const result = await entities.query('<entityId>', {
* selectedFields: ['name', 'email'],
* filterGroup: {
* logicalOperator: 0,
* queryFilters: [{ fieldName: 'status', operator: '=', value: 'active' }]
* },
* limit: 50
* });
*
* // Cross-entity join query
* const joinResult = await entities.query('<entityId>', {
* selectedFields: ['Orders.orderId', 'Customer.name'],
* joins: [{
* type: 'INNER',
* entity: 'Customer',
* on: { left: 'customerId', right: 'Id' }
* }],
* sortOptions: [{ fieldName: 'Orders.createdDate', isDescending: true }],
* limit: 100
* });
*
* // Aggregate query
* const stats = await entities.query('<entityId>', {
* aggregates: [
* { function: 'COUNT', field: 'Id', alias: 'totalOrders' },
* { function: 'SUM', field: 'amount', alias: 'totalAmount' }
* ],
* groupBy: ['status']
Comment on lines +510 to +515
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example uses aggregates without selectedFields. Wouldn't this error out?

* });
Comment on lines +487 to +516
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we have defined enums for these values, why not use them in the examples as well?
line 491 logicalOperator: 0, -> QueryLogicalOperator.AND
line 501 type: 'INNER', -> QueryJoinType.INNER
and the aggregate function on line 512 and 513

* ```
*/
query(entityId: string, options?: EntityQueryOptions): Promise<EntityQueryResponse>;
}

/**
Expand Down Expand Up @@ -576,6 +632,21 @@ export interface EntityMethods {
*/
deleteAttachment(recordId: string, fieldName: string): Promise<EntityDeleteAttachmentResponse>;

/**
*
* @experimental
*
* /// warning
Preview: This method is experimental and may change or be removed in future releases.
///
*
* Queries this entity with support for joins, aggregates, filters, grouping, and sorting
*
* @param options - Query options
* @returns Promise resolving to query response with matching records
*/
query(options?: EntityQueryOptions): Promise<EntityQueryResponse>;

/**
* @deprecated Use {@link insertRecord} instead.
* @hidden
Expand Down Expand Up @@ -691,6 +762,12 @@ function createEntityMethods(entityData: RawEntityGetResponse, service: EntitySe
return service.deleteAttachment(entityData.id, recordId, fieldName);
},

async query(options?: EntityQueryOptions): Promise<EntityQueryResponse> {
if (!entityData.id) throw new Error('Entity ID is undefined');

return service.query(entityData.id, options);
},

async insert(data: Record<string, any>, options?: EntityInsertOptions): Promise<EntityInsertResponse> {
return this.insertRecord(data, options);
},
Expand Down
206 changes: 206 additions & 0 deletions src/models/data-fabric/entities.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,212 @@ export interface EntityUpdateResponse extends EntityOperationResponse {}
*/
export interface EntityDeleteResponse extends EntityOperationResponse {}

/**
*
* @experimental
*
* /// warning
Preview: This type is experimental and may change or be removed in future releases.
///
*
* Aggregate function types for query operations
*/
export enum QueryAggregateFunction {
COUNT = "COUNT",
SUM = "SUM",
AVG = "AVG",
MIN = "MIN",
MAX = "MAX",
}

/**
*
* @experimental
*
* /// warning
Preview: This type is experimental and may change or be removed in future releases.
///
*
* Join types for cross-entity query operations
*/
export enum QueryJoinType {
INNER = "INNER",
LEFT = "LEFT",
RIGHT = "RIGHT",
FULL = "FULL",
}

/**
*
* @experimental
*
* /// warning
Preview: This type is experimental and may change or be removed in future releases.
///
*
* Logical operators for combining query filters
*/
export enum QueryLogicalOperator {
AND = 0,
OR = 1,
}

/**
*
* @experimental
*
* /// warning
Preview: This type is experimental and may change or be removed in future releases.
///
*
* An aggregate operation in a query
*/
export interface QueryAggregate {
/** Aggregate function to apply */
function: QueryAggregateFunction;
/** Field to aggregate on */
field: string;
/** Alias for the aggregated result */
alias: string;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question : Is alias always required by the API?

}

/**
*
* @experimental
*
* /// warning
Preview: This type is experimental and may change or be removed in future releases.
///
*
* A join condition for cross-entity queries
*/
export interface QueryJoinCondition {
/** Left side field name (from the primary entity) */
left: string;
/** Right side field name (from the joined entity) */
right: string;
}

/**
*
* @experimental
*
* /// warning
Preview: This type is experimental and may change or be removed in future releases.
///
*
* A join clause for cross-entity queries
*/
export interface QueryJoin {
/** Type of join */
type: QueryJoinType;
/** Name of the entity to join */
entity: string;
/** Join condition specifying left and right field mappings */
on: QueryJoinCondition;
}

/**
*
* @experimental
*
* /// warning
Preview: This type is experimental and may change or be removed in future releases.
///
*
* A single query filter condition
*/
export interface QueryFilter {
/** Field name to filter on */
fieldName: string;
/** Comparison operator: "=", "!=", ">", "<", ">=", "<=", "contains", "startswith", "endswith" (lowercase for string operators) */
operator: string;
Comment on lines +312 to +313
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we have this as an enum?

/** Value to compare against */
value: unknown;
}

/**
*
* @experimental
*
* /// warning
Preview: This type is experimental and may change or be removed in future releases.
///
*
* A group of query filters combined with a logical operator
*/
export interface QueryFilterGroup {
/** Logical operator to combine filters (0 = AND, 1 = OR) */
logicalOperator: QueryLogicalOperator;
/** Array of filter conditions */
queryFilters: QueryFilter[];
/** Nested filter groups for complex conditions */
queryFilterGroups?: QueryFilterGroup[];
}

/**
*
* @experimental
*
* /// warning
Preview: This type is experimental and may change or be removed in future releases.
///
*
* Sort option for query results
*/
export interface QuerySortOption {
/** Field name to sort by */
fieldName: string;
/** Whether to sort in descending order (default: false) */
isDescending?: boolean;
}

/**
*
* @experimental
*
* /// warning
Preview: This type is experimental and may change or be removed in future releases.
///
*
* Options for querying an entity with joins, aggregates, and filters
*/
export interface EntityQueryOptions {
/** Fields to include in the result set. Required when using aggregates — must include the groupBy fields. */
selectedFields?: string[];
/** Aggregate operations to perform. Requires selectedFields to include the groupBy fields. */
aggregates?: QueryAggregate[];
/** Cross-entity join clauses. Joined entity fields can be referenced as "entityName.fieldName" in selectedFields. */
joins?: QueryJoin[];
/** Filter conditions */
filterGroup?: QueryFilterGroup;
/** Fields to group results by (used with aggregates). These fields must also appear in selectedFields. */
groupBy?: string[];
/** Sort options for result ordering */
sortOptions?: QuerySortOption[];
/** Starting offset for pagination (default: 0) */
start?: number;
/** Maximum number of records to return (default: 100) */
limit?: number;
}
Comment on lines +377 to +381
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SDK already supports pagination. Can't we use that instead?


/**
*
* @experimental
*
* /// warning
Preview: This type is experimental and may change or be removed in future releases.
///
*
* Response from a query operation
*/
export interface EntityQueryResponse {
/** Array of matching records */
value: Record<string, unknown>[];
/** Total count of matching records (before pagination) */
totalRecordCount?: number;
}

/**
* Entity type enum
*/
Expand Down
Loading
Loading