Skip to content

Commit

Permalink
move debounceAsync to a package
Browse files Browse the repository at this point in the history
  • Loading branch information
maximpn committed Nov 21, 2024
1 parent 45bfabc commit e9ee5c7
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 49 deletions.
1 change: 1 addition & 0 deletions packages/kbn-securitysolution-utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export * from './src/axios';
export * from './src/transform_data_to_ndjson';
export * from './src/path_validations';
export * from './src/esql';
export * from './src/debounce_async/debounce_async';
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { debounceAsync } from './debounce_async';

jest.useFakeTimers({ legacyFakeTimers: true });

describe('debounceAsync', () => {
let fn: jest.Mock;

beforeEach(() => {
fn = jest.fn().mockResolvedValueOnce('first');
});

it('resolves with the underlying invocation result', async () => {
const fn = jest.fn().mockResolvedValueOnce('first');

const debounced = debounceAsync(fn, 0);
const promise = debounced();
jest.runOnlyPendingTimers();
Expand All @@ -25,6 +23,8 @@ describe('debounceAsync', () => {
});

it('resolves intermediate calls when the next invocation resolves', async () => {
const fn = jest.fn().mockResolvedValueOnce('first');

const debounced = debounceAsync(fn, 200);
fn.mockResolvedValueOnce('second');

Expand All @@ -39,6 +39,8 @@ describe('debounceAsync', () => {
});

it('debounces the function', async () => {
const fn = jest.fn().mockResolvedValueOnce('first');

const debounced = debounceAsync(fn, 200);

debounced();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

/**
* Unlike lodash's debounce, which resolves intermediate calls with the most
* recent value, this implementation waits to resolve intermediate calls until
* the next invocation resolves.
*
* @param fn an async function
*
* @returns A debounced async function that resolves on the next invocation
*/
export function debounceAsync<Args extends unknown[], Result>(
fn: (...args: Args) => Result,
intervalMs: number
): (...args: Args) => Promise<Awaited<Result>> {
let timeoutId: ReturnType<typeof setTimeout> | undefined;
let resolve: (value: Awaited<Result>) => void;
let promise = new Promise<Awaited<Result>>((_resolve) => {
resolve = _resolve;
});

return (...args) => {
if (timeoutId) {
clearTimeout(timeoutId);
}

timeoutId = setTimeout(async () => {
resolve(await fn(...args));
promise = new Promise((_resolve) => {
resolve = _resolve;
});
}, intervalMs);

return promise;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@

import React, { useMemo } from 'react';
import type { DataViewBase } from '@kbn/es-query';
import { debounceAsync } from '@kbn/securitysolution-utils';
import type { FormData, FieldConfig, ValidationFuncArg } from '../../../../shared_imports';
import { UseMultiFields } from '../../../../shared_imports';
import type { EqlFieldsComboBoxOptions, EqlOptions } from '../../../../../common/search_strategy';
import { queryRequiredValidatorFactory } from '../../../rule_creation_ui/validators/query_required_validator_factory';
import { debounceAsync } from '../../../rule_creation_ui/validators/debounce_async';
import { eqlQueryValidatorFactory } from './eql_query_validator_factory';
import { EqlQueryBar } from './eql_query_bar';
import * as i18n from './translations';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { isEmpty } from 'lodash';
import { i18n } from '@kbn/i18n';
import { EuiText } from '@elastic/eui';
import React from 'react';

import { debounceAsync } from '@kbn/securitysolution-utils';
import {
singleEntryThreat,
containsInvalidItems,
Expand All @@ -28,7 +28,6 @@ import { MAX_NUMBER_OF_NEW_TERMS_FIELDS } from '../../../../../common/constants'
import { isMlRule } from '../../../../../common/machine_learning/helpers';
import type { ERROR_CODE, FormSchema, ValidationFunc } from '../../../../shared_imports';
import { FIELD_TYPES, fieldValidators } from '../../../../shared_imports';
import { debounceAsync } from '../../validators/debounce_async';
import type { DefineStepRule } from '../../../../detections/pages/detection_engine/rules/types';
import { DataSourceType } from '../../../../detections/pages/detection_engine/rules/types';
import { esqlValidator } from '../../../rule_creation/logic/esql_validator';
Expand Down

This file was deleted.

0 comments on commit e9ee5c7

Please sign in to comment.