@@ -13,11 +13,14 @@ import { randomUUID } from "crypto";
1313import { newOrchestrationState } from "../orchestration" ;
1414import { OrchestrationState } from "../orchestration/orchestration-state" ;
1515import { GrpcClient } from "./client-grpc" ;
16- import { OrchestrationStatus , toProtobuf } from "../orchestration/enum/orchestration-status.enum" ;
16+ import { OrchestrationStatus , toProtobuf , fromProtobuf } from "../orchestration/enum/orchestration-status.enum" ;
1717import { TimeoutError } from "../exception/timeout-error" ;
1818import { PurgeResult } from "../orchestration/orchestration-purge-result" ;
1919import { PurgeInstanceCriteria } from "../orchestration/orchestration-purge-criteria" ;
2020import { callWithMetadata , MetadataGenerator } from "../utils/grpc-helper.util" ;
21+ import { OrchestrationQuery , ListInstanceIdsOptions , DEFAULT_PAGE_SIZE } from "../orchestration/orchestration-query" ;
22+ import { Page , AsyncPageable , createAsyncPageable } from "../orchestration/page" ;
23+ import { FailureDetails } from "../task/failure-details" ;
2124
2225// Re-export MetadataGenerator for backward compatibility
2326export { MetadataGenerator } from "../utils/grpc-helper.util" ;
@@ -384,4 +387,241 @@ export class TaskHubGrpcClient {
384387 }
385388 return new PurgeResult ( res . getDeletedinstancecount ( ) ) ;
386389 }
390+
391+ /**
392+ * Queries orchestration instances and returns an async iterable of results.
393+ *
394+ * This method supports querying orchestration instances by various filter criteria including
395+ * creation time range, runtime status, instance ID prefix, and task hub names.
396+ *
397+ * The results are returned as an AsyncPageable that supports both iteration over individual
398+ * items and iteration over pages.
399+ *
400+ * @example
401+ * ```typescript
402+ * // Iterate over all matching instances
403+ * const pageable = client.getAllInstances({ statuses: [OrchestrationStatus.COMPLETED] });
404+ * for await (const instance of pageable) {
405+ * console.log(instance.instanceId);
406+ * }
407+ *
408+ * // Iterate over pages
409+ * for await (const page of pageable.asPages()) {
410+ * console.log(`Page has ${page.values.length} items`);
411+ * }
412+ * ```
413+ *
414+ * @param filter - Optional filter criteria for the query.
415+ * @returns An AsyncPageable of OrchestrationState objects.
416+ */
417+ getAllInstances ( filter ?: OrchestrationQuery ) : AsyncPageable < OrchestrationState > {
418+ return createAsyncPageable < OrchestrationState > (
419+ async ( continuationToken ?: string , pageSize ?: number ) : Promise < Page < OrchestrationState > > => {
420+ const req = new pb . QueryInstancesRequest ( ) ;
421+ const query = new pb . InstanceQuery ( ) ;
422+
423+ // Set created time range
424+ if ( filter ?. createdFrom ) {
425+ const timestamp = new Timestamp ( ) ;
426+ timestamp . fromDate ( filter . createdFrom ) ;
427+ query . setCreatedtimefrom ( timestamp ) ;
428+ }
429+
430+ if ( filter ?. createdTo ) {
431+ const timestamp = new Timestamp ( ) ;
432+ timestamp . fromDate ( filter . createdTo ) ;
433+ query . setCreatedtimeto ( timestamp ) ;
434+ }
435+
436+ // Set runtime statuses
437+ if ( filter ?. statuses ) {
438+ for ( const status of filter . statuses ) {
439+ query . addRuntimestatus ( toProtobuf ( status ) ) ;
440+ }
441+ }
442+
443+ // Set task hub names
444+ if ( filter ?. taskHubNames ) {
445+ for ( const name of filter . taskHubNames ) {
446+ const stringValue = new StringValue ( ) ;
447+ stringValue . setValue ( name ) ;
448+ query . addTaskhubnames ( stringValue ) ;
449+ }
450+ }
451+
452+ // Set instance ID prefix
453+ if ( filter ?. instanceIdPrefix ) {
454+ const prefixValue = new StringValue ( ) ;
455+ prefixValue . setValue ( filter . instanceIdPrefix ) ;
456+ query . setInstanceidprefix ( prefixValue ) ;
457+ }
458+
459+ // Set page size
460+ const effectivePageSize = pageSize ?? filter ?. pageSize ?? DEFAULT_PAGE_SIZE ;
461+ query . setMaxinstancecount ( effectivePageSize ) ;
462+
463+ // Set continuation token (use provided or from filter)
464+ const token = continuationToken ?? filter ?. continuationToken ;
465+ if ( token ) {
466+ const tokenValue = new StringValue ( ) ;
467+ tokenValue . setValue ( token ) ;
468+ query . setContinuationtoken ( tokenValue ) ;
469+ }
470+
471+ // Set fetch inputs and outputs
472+ query . setFetchinputsandoutputs ( filter ?. fetchInputsAndOutputs ?? false ) ;
473+
474+ req . setQuery ( query ) ;
475+
476+ const response = await callWithMetadata < pb . QueryInstancesRequest , pb . QueryInstancesResponse > (
477+ this . _stub . queryInstances . bind ( this . _stub ) ,
478+ req ,
479+ this . _metadataGenerator ,
480+ ) ;
481+
482+ const states : OrchestrationState [ ] = [ ] ;
483+ const orchestrationStateList = response . getOrchestrationstateList ( ) ;
484+ for ( const state of orchestrationStateList ) {
485+ const orchestrationState = this . _createOrchestrationStateFromProto ( state , filter ?. fetchInputsAndOutputs ?? false ) ;
486+ if ( orchestrationState ) {
487+ states . push ( orchestrationState ) ;
488+ }
489+ }
490+
491+ const responseContinuationToken = response . getContinuationtoken ( ) ?. getValue ( ) ;
492+ return new Page ( states , responseContinuationToken ) ;
493+ } ,
494+ ) ;
495+ }
496+
497+ /**
498+ * Lists orchestration instance IDs that match the specified runtime status
499+ * and completed time range, using key-based pagination.
500+ *
501+ * This method is optimized for listing instance IDs without fetching full instance metadata,
502+ * making it more efficient when only instance IDs are needed.
503+ *
504+ * @example
505+ * ```typescript
506+ * // Get first page of completed instances
507+ * const page = await client.listInstanceIds({
508+ * runtimeStatus: [OrchestrationStatus.COMPLETED],
509+ * pageSize: 50
510+ * });
511+ *
512+ * // Get next page using the continuation key
513+ * if (page.hasMoreResults) {
514+ * const nextPage = await client.listInstanceIds({
515+ * runtimeStatus: [OrchestrationStatus.COMPLETED],
516+ * pageSize: 50,
517+ * lastInstanceKey: page.continuationToken
518+ * });
519+ * }
520+ * ```
521+ *
522+ * @param options - Optional filter criteria and pagination options.
523+ * @returns A Promise that resolves to a Page of instance IDs.
524+ */
525+ async listInstanceIds ( options ?: ListInstanceIdsOptions ) : Promise < Page < string > > {
526+ const req = new pb . ListInstanceIdsRequest ( ) ;
527+
528+ // Set page size
529+ const pageSize = options ?. pageSize ?? DEFAULT_PAGE_SIZE ;
530+ req . setPagesize ( pageSize ) ;
531+
532+ // Set last instance key (continuation token)
533+ if ( options ?. lastInstanceKey ) {
534+ const keyValue = new StringValue ( ) ;
535+ keyValue . setValue ( options . lastInstanceKey ) ;
536+ req . setLastinstancekey ( keyValue ) ;
537+ }
538+
539+ // Set runtime statuses
540+ if ( options ?. runtimeStatus ) {
541+ for ( const status of options . runtimeStatus ) {
542+ req . addRuntimestatus ( toProtobuf ( status ) ) ;
543+ }
544+ }
545+
546+ // Set completed time range
547+ if ( options ?. completedTimeFrom ) {
548+ const timestamp = new Timestamp ( ) ;
549+ timestamp . fromDate ( options . completedTimeFrom ) ;
550+ req . setCompletedtimefrom ( timestamp ) ;
551+ }
552+
553+ if ( options ?. completedTimeTo ) {
554+ const timestamp = new Timestamp ( ) ;
555+ timestamp . fromDate ( options . completedTimeTo ) ;
556+ req . setCompletedtimeto ( timestamp ) ;
557+ }
558+
559+ const response = await callWithMetadata < pb . ListInstanceIdsRequest , pb . ListInstanceIdsResponse > (
560+ this . _stub . listInstanceIds . bind ( this . _stub ) ,
561+ req ,
562+ this . _metadataGenerator ,
563+ ) ;
564+
565+ const instanceIds = response . getInstanceidsList ( ) ;
566+ const lastInstanceKey = response . getLastinstancekey ( ) ?. getValue ( ) ;
567+
568+ return new Page ( instanceIds , lastInstanceKey ) ;
569+ }
570+
571+ /**
572+ * Helper method to create an OrchestrationState from a protobuf OrchestrationState.
573+ */
574+ private _createOrchestrationStateFromProto (
575+ protoState : pb . OrchestrationState ,
576+ fetchPayloads : boolean ,
577+ ) : OrchestrationState | undefined {
578+ const instanceId = protoState . getInstanceid ( ) ;
579+ const name = protoState . getName ( ) ;
580+ const runtimeStatus = protoState . getOrchestrationstatus ( ) ;
581+ const createdTimestamp = protoState . getCreatedtimestamp ( ) ;
582+ const lastUpdatedTimestamp = protoState . getLastupdatedtimestamp ( ) ;
583+
584+ if ( ! instanceId ) {
585+ return undefined ;
586+ }
587+
588+ const createdAt = createdTimestamp ? createdTimestamp . toDate ( ) : new Date ( 0 ) ;
589+ const lastUpdatedAt = lastUpdatedTimestamp ? lastUpdatedTimestamp . toDate ( ) : new Date ( 0 ) ;
590+
591+ // Map proto status to our status enum using the existing conversion function
592+ const status = fromProtobuf ( runtimeStatus ) ;
593+
594+ let serializedInput : string | undefined ;
595+ let serializedOutput : string | undefined ;
596+ let serializedCustomStatus : string | undefined ;
597+
598+ if ( fetchPayloads ) {
599+ serializedInput = protoState . getInput ( ) ?. getValue ( ) ;
600+ serializedOutput = protoState . getOutput ( ) ?. getValue ( ) ;
601+ serializedCustomStatus = protoState . getCustomstatus ( ) ?. getValue ( ) ;
602+ }
603+
604+ // Extract failure details if present
605+ let failureDetails ;
606+ const protoFailureDetails = protoState . getFailuredetails ( ) ;
607+ if ( protoFailureDetails ) {
608+ failureDetails = new FailureDetails (
609+ protoFailureDetails . getErrormessage ( ) ,
610+ protoFailureDetails . getErrortype ( ) ,
611+ protoFailureDetails . getStacktrace ( ) ?. getValue ( ) ,
612+ ) ;
613+ }
614+
615+ return new OrchestrationState (
616+ instanceId ,
617+ name ?? "" ,
618+ status ,
619+ createdAt ,
620+ lastUpdatedAt ,
621+ serializedInput ,
622+ serializedOutput ,
623+ serializedCustomStatus ,
624+ failureDetails ,
625+ ) ;
626+ }
387627}
0 commit comments