Skip to content

Commit

Permalink
feat(clients): add waitForAppTask helper (#3158)
Browse files Browse the repository at this point in the history
Co-authored-by: shortcuts <[email protected]>
  • Loading branch information
millotp and shortcuts authored Jun 11, 2024
1 parent df55415 commit c7f5de9
Show file tree
Hide file tree
Showing 16 changed files with 415 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,33 @@ public GetTaskResponse WaitForTask(string indexName, long taskId, int maxRetries
Func<int, int> timeout = null, RequestOptions requestOptions = null, CancellationToken ct = default) =>
AsyncHelper.RunSync(() => WaitForTaskAsync(indexName, taskId, maxRetries, timeout, requestOptions, ct));

/// <summary>
/// Wait for an application-level task to complete with `taskID`.
/// </summary>
/// <param name="taskId">The `taskID` returned in the method response.</param>
/// <param name="maxRetries">The maximum number of retry. 50 by default. (optional)</param>
/// <param name="timeout">The function to decide how long to wait between retries. Math.Min(retryCount * 200, 5000) by default. (optional)</param>
/// <param name="requestOptions">The requestOptions to send along with the query, they will be merged with the transporter requestOptions. (optional)</param>
/// <param name="ct">Cancellation token (optional)</param>
public async Task<GetTaskResponse> WaitForAppTaskAsync(long taskId, int maxRetries = DefaultMaxRetries,
Func<int, int> timeout = null, RequestOptions requestOptions = null, CancellationToken ct = default)
{
return await RetryUntil(async () => await GetAppTaskAsync(taskId, requestOptions, ct),
resp => resp.Status == Models.Search.TaskStatus.Published, maxRetries, timeout, ct).ConfigureAwait(false);
}

/// <summary>
/// Wait for an application-level task to complete with `taskID`. (Synchronous version)
/// </summary>
/// <param name="taskId">The `taskID` returned in the method response.</param>
/// <param name="maxRetries">The maximum number of retry. 50 by default. (optional)</param>
/// <param name="timeout">The function to decide how long to wait between retries. Math.Min(retryCount * 200, 5000) by default. (optional)</param>
/// <param name="requestOptions">The requestOptions to send along with the query, they will be merged with the transporter requestOptions. (optional)</param>
/// <param name="ct">Cancellation token (optional)</param>
public GetTaskResponse WaitForAppTask(long taskId, int maxRetries = DefaultMaxRetries,
Func<int, int> timeout = null, RequestOptions requestOptions = null, CancellationToken ct = default) =>
AsyncHelper.RunSync(() => WaitForAppTaskAsync(taskId, maxRetries, timeout, requestOptions, ct));

/// <summary>
/// Helper method that waits for an API key task to be processed.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,22 @@ extension WaitTask on SearchClient {
);
}

/// Wait for an application-level [taskID] to complete before executing the next line of code.
Future<void> waitAppTask({
required int taskID,
WaitParams params = const WaitParams(),
RequestOptions? requestOptions,
}) async {
await _waitUntil(
params: params,
retry: () => getAppTask(
taskID: taskID,
requestOptions: requestOptions,
),
until: (response) => response.status == TaskStatus.published,
);
}

/// Wait on an API key creation operation.
Future<void> waitKeyCreation({
required String key,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,34 @@ public suspend fun SearchClient.waitTask(
)
}

/**
* Wait for an application-level [taskID] to complete before executing the next line of code.
*
* @param taskID The ID of the task to wait for.
* @param timeout If specified, the method will throw a
* [kotlinx.coroutines.TimeoutCancellationException] after the timeout value in milliseconds is
* elapsed.
* @param maxRetries maximum number of retry attempts.
* @param requestOptions additional request configuration.
*/
public suspend fun SearchClient.waitAppTask(
taskID: Long,
maxRetries: Int = 50,
timeout: Duration = Duration.INFINITE,
initialDelay: Duration = 200.milliseconds,
maxDelay: Duration = 5.seconds,
requestOptions: RequestOptions? = null,
): TaskStatus {
return retryUntil(
timeout = timeout,
maxRetries = maxRetries,
initialDelay = initialDelay,
maxDelay = maxDelay,
retry = { getAppTask(taskID, requestOptions).status },
until = { it == TaskStatus.Published },
)
}

/**
* Wait on an API key update operation.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,29 @@ package object extension {
)
}

/** Wait for an application-level taskID to complete before executing the next line of code.
*
* @param taskID
* The ID of the task to wait for.
* @param maxRetries
* maximum number of retry attempts.
* @param requestOptions
* additional request configuration.
*/
def waitAppTask(
taskID: Long,
delay: Long => Long = DEFAULT_DELAY,
maxRetries: Int = 50,
requestOptions: Option[RequestOptions] = None
)(implicit ec: ExecutionContext): Future[TaskStatus] = {
retryUntil(
retry = () => client.getAppTask(taskID, requestOptions).map(_.status),
until = (status: TaskStatus) => status == TaskStatus.Published,
maxRetries = maxRetries,
delay = delay
)
}

/** Wait on an API key update operation.
*
* @param key
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,47 @@ public extension SearchClient {
)
}

/// Wait for an application-level task to complete
/// - parameter taskID: The id of the task to wait for
/// - parameter maxRetries: The maximum number of retries
/// - parameter initialDelay: The initial delay between retries
/// - parameter maxDelay: The maximum delay between retries
/// - returns: GetTaskResponse
@discardableResult
func waitForAppTask(
with taskID: Int64,
maxRetries: Int = 50,
timeout: (Int) -> TimeInterval = { count in
min(TimeInterval(count) * 0.2, 5)
},
requestOptions: RequestOptions? = nil
) async throws -> GetTaskResponse {
var retryCount = 0

return try await createIterable(
execute: { _ in
try await self.getAppTask(taskID: taskID, requestOptions: requestOptions)
},
validate: { response in
response.status == SearchTaskStatus.published
},
aggregator: { _ in
retryCount += 1
},
timeout: {
timeout(retryCount)
},
error: IterableError(
validate: { _ in
retryCount >= maxRetries
},
message: { _ in
"The maximum number of retries exceeded. (\(retryCount)/\(maxRetries))"
}
)
)
}

/// Wait for an API key to be available
/// - parameter key: The key to wait for
/// - parameter operation: The type of operation
Expand Down
78 changes: 78 additions & 0 deletions templates/go/search_helpers.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,84 @@ func (c *APIClient) WaitForTaskWithContext(
)
}

/*
WaitForAppTask waits for an application-level task to be published.
Wraps WaitForAppTask with context.Background().
It returns the task response if the operation was successful.
It returns an error if the operation failed.

The maxRetries parameter is the maximum number of retries.
The initialDelay parameter is the initial delay between each retry.
The maxDelay parameter is the maximum delay between each retry.

@param taskID int64 - Task ID.
@param maxRetries *int - Maximum number of retries.
@param initialDelay *time.Duration - Initial delay between retries.
@param maxDelay *time.Duration - Maximum delay between retries.
@param opts ...Option - Optional parameters for the request.
@return *GetTaskResponse - Task response.
@return error - Error if any.
*/
func (c *APIClient) WaitForAppTask(
taskID int64,
maxRetries *int,
initialDelay *time.Duration,
maxDelay *time.Duration,
opts ...Option,
) (*GetTaskResponse, error) {
return c.WaitForAppTaskWithContext(
context.Background(),
taskID,
maxRetries,
initialDelay,
maxDelay,
opts...,
)
}

/*
WaitForAppTaskWithContext waits for an application-level task to be published.
It returns the task response if the operation was successful.
It returns an error if the operation failed.

The maxRetries parameter is the maximum number of retries.
The initialDelay parameter is the initial delay between each retry.
The maxDelay parameter is the maximum delay between each retry.

@param ctx context.Context - The context that will be drilled down to the actual request.
@param taskID int64 - Task ID.
@param maxRetries *int - Maximum number of retries.
@param initialDelay *time.Duration - Initial delay between retries.
@param maxDelay *time.Duration - Maximum delay between retries.
@param opts ...Option - Optional parameters for the request.
@return *GetTaskResponse - Task response.
@return error - Error if any.
*/
func (c *APIClient) WaitForAppTaskWithContext(
ctx context.Context,
taskID int64,
maxRetries *int,
initialDelay *time.Duration,
maxDelay *time.Duration,
opts ...Option,
) (*GetTaskResponse, error) {
return utils.RetryUntil( //nolint:wrapcheck
func() (*GetTaskResponse, error) {
return c.GetAppTaskWithContext(ctx, c.NewApiGetAppTaskRequest(taskID), opts...)
},
func(response *GetTaskResponse, err error) bool {
if err != nil || response == nil {
return false
}

return response.Status == TASKSTATUS_PUBLISHED
},
maxRetries,
initialDelay,
maxDelay,
)
}

/*
WaitForApiKey waits for an API key to be created, deleted or updated.
It returns the API key response if the operation was successful.
Expand Down
51 changes: 51 additions & 0 deletions templates/java/api_helpers.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,57 @@ public void waitForTask(String indexName, Long taskID) {
this.waitForTask(indexName, taskID, TaskUtils.DEFAULT_MAX_RETRIES, TaskUtils.DEFAULT_TIMEOUT, null);
}

/**
* Helper: Wait for a application-level task to complete with `taskID`.
*
* @param taskID The `taskID` returned in the method response.
* @param maxRetries The maximum number of retry. 50 by default. (optional)
* @param timeout The function to decide how long to wait between retries. min(retries * 200,
* 5000) by default. (optional)
* @param requestOptions The requestOptions to send along with the query, they will be merged with
* the transporter requestOptions. (optional)
*/
public void waitForAppTask(Long taskID, int maxRetries, IntUnaryOperator timeout, RequestOptions requestOptions) {
TaskUtils.retryUntil(
() -> this.getAppTask(taskID, requestOptions),
(GetTaskResponse task) -> task.getStatus() == TaskStatus.PUBLISHED,
maxRetries,
timeout
);
}

/**
* Helper: Wait for an application-level task to complete with `taskID`.
*
* @param taskID The `taskID` returned in the method response.
* @param requestOptions The requestOptions to send along with the query, they will be merged with
* the transporter requestOptions. (optional)
*/
public void waitForAppTask(Long taskID, RequestOptions requestOptions) {
this.waitForAppTask(taskID, TaskUtils.DEFAULT_MAX_RETRIES, TaskUtils.DEFAULT_TIMEOUT, requestOptions);
}

/**
* Helper: Wait for an application-level task to complete with `taskID`.
*
* @param taskID The `taskID` returned in the method response.
* @param maxRetries The maximum number of retry. 50 by default. (optional)
* @param timeout The function to decide how long to wait between retries. min(retries * 200,
* 5000) by default. (optional)
*/
public void waitForAppTask(Long taskID, int maxRetries, IntUnaryOperator timeout) {
this.waitForAppTask(taskID, maxRetries, timeout, null);
}

/**
* Helper: Wait for an application-level task to complete with `taskID`.
*
* @param taskID The `taskID` returned in the method response.
*/
public void waitForAppTask(Long taskID) {
this.waitForAppTask(taskID, TaskUtils.DEFAULT_MAX_RETRIES, TaskUtils.DEFAULT_TIMEOUT, null);
}

/**
* Helper: Wait for an API key to be added, updated or deleted based on a given `operation`.
*
Expand Down
34 changes: 34 additions & 0 deletions templates/javascript/clients/client/api/helpers.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,40 @@ waitForTask(
});
},

/**
* Helper: Wait for an application-level task to complete for a given `taskID`.
*
* @summary Helper method that waits for a task to be published (completed).
* @param waitForAppTaskOptions - The `waitForTaskOptions` object.
* @param waitForAppTaskOptions.taskID - The `taskID` returned in the method response.
* @param waitForAppTaskOptions.maxRetries - The maximum number of retries. 50 by default.
* @param waitForAppTaskOptions.timeout - The function to decide how long to wait between retries.
* @param requestOptions - The requestOptions to send along with the query, they will be forwarded to the `getTask` method and merged with the transporter requestOptions.
*/
waitForAppTask(
{
taskID,
maxRetries = 50,
timeout = (retryCount: number): number =>
Math.min(retryCount * 200, 5000),
}: WaitForAppTaskOptions,
requestOptions?: RequestOptions
): Promise<GetTaskResponse> {
let retryCount = 0;
return createIterablePromise({
func: () => this.getAppTask({ taskID }, requestOptions),
validate: (response) => response.status === 'published',
aggregator: () => (retryCount += 1),
error: {
validate: () => retryCount >= maxRetries,
message: () =>
`The maximum number of retries exceeded. (${retryCount}/${maxRetries})`,
},
timeout: () => timeout(retryCount),
});
},

/**
* Helper: Wait for an API key to be added, updated or deleted based on a given `operation`.
*
Expand Down
1 change: 1 addition & 0 deletions templates/javascript/clients/client/api/imports.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import type {
ReplaceAllObjectsOptions,
WaitForApiKeyOptions,
WaitForTaskOptions,
WaitForAppTaskOptions,
{{/isSearchClient}}
{{#operation}}
{{#vendorExtensions}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,20 @@ type WaitForOptions = Partial<{
timeout: (retryCount: number) => number;
}>;

export type WaitForTaskOptions = WaitForOptions & {
/**
* The `indexName` where the operation was performed.
*/
indexName: string;
export type WaitForAppTaskOptions = WaitForOptions & {
/**
* The `taskID` returned by the method response.
*/
taskID: number;
};

export type WaitForTaskOptions = WaitForAppTaskOptions & {
/**
* The `indexName` where the operation was performed.
*/
indexName: string;
};

export type WaitForApiKeyOptions = WaitForOptions & {
/**
* The API Key.
Expand Down Expand Up @@ -148,4 +151,4 @@ export type ReplaceAllObjectsOptions = {
}
{{/isSearchClient}}

{{/apiInfo.apis.0}}
{{/apiInfo.apis.0}}
Loading

0 comments on commit c7f5de9

Please sign in to comment.