mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Security Solution] kbn package for generic hook utils (#101976)
* Adds boilerplate for new hook-utils package * Move existing, identified utils into our hook-utils package Updates references, and fixes a few missing config that were preventing packages from building. * Extracts a common type and adds a little more JSdoc for clarity * Adds new useObservable hook Similar to useAsync (a nearly identical interface), this is meant to wrap a thunk returning an observable, allowing conditional invocation and progressive updates as the observable continues to emit. * Remove orphaned test This function (and its tests) were moved to the hook-utils package; this was simply missed. * Remove optional chaining from kbn package The build system does not currently support these typescript features. While a valid fix would also have been to build separate browser and node targets a la #99390, the use here was very minimal and so changing to a supported syntax was the most pragmatic fix. * Update old reference in test file Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
a0effa1ebd
commit
ac07ebba87
37 changed files with 424 additions and 97 deletions
|
@ -87,6 +87,7 @@ yarn kbn watch-bazel
|
|||
- @kbn/monaco
|
||||
- @kbn/rule-data-utils
|
||||
- @kbn/securitysolution-es-utils
|
||||
- @kbn/securitysolution-hook-utils
|
||||
- @kbn/securitysolution-io-ts-alerting-types
|
||||
- @kbn/securitysolution-io-ts-list-types
|
||||
- @kbn/securitysolution-io-ts-types
|
||||
|
|
|
@ -141,6 +141,7 @@
|
|||
"@kbn/rule-data-utils": "link:bazel-bin/packages/kbn-rule-data-utils",
|
||||
"@kbn/securitysolution-list-constants": "link:bazel-bin/packages/kbn-securitysolution-list-constants",
|
||||
"@kbn/securitysolution-es-utils": "link:bazel-bin/packages/kbn-securitysolution-es-utils",
|
||||
"@kbn/securitysolution-hook-utils": "link:bazel-bin/packages/kbn-securitysolution-hook-utils",
|
||||
"@kbn/securitysolution-io-ts-types": "link:bazel-bin/packages/kbn-securitysolution-io-ts-types",
|
||||
"@kbn/securitysolution-io-ts-alerting-types": "link:bazel-bin/packages/kbn-securitysolution-io-ts-alerting-types",
|
||||
"@kbn/securitysolution-io-ts-list-types": "link:bazel-bin/packages/kbn-securitysolution-io-ts-list-types",
|
||||
|
|
|
@ -40,6 +40,7 @@ filegroup(
|
|||
"//packages/kbn-securitysolution-list-utils:build",
|
||||
"//packages/kbn-securitysolution-utils:build",
|
||||
"//packages/kbn-securitysolution-es-utils:build",
|
||||
"//packages/kbn-securitysolution-hook-utils:build",
|
||||
"//packages/kbn-server-http-tools:build",
|
||||
"//packages/kbn-server-route-repository:build",
|
||||
"//packages/kbn-std:build",
|
||||
|
|
87
packages/kbn-securitysolution-hook-utils/BUILD.bazel
Normal file
87
packages/kbn-securitysolution-hook-utils/BUILD.bazel
Normal file
|
@ -0,0 +1,87 @@
|
|||
load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project")
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm")
|
||||
|
||||
PKG_BASE_NAME = "kbn-securitysolution-hook-utils"
|
||||
|
||||
PKG_REQUIRE_NAME = "@kbn/securitysolution-hook-utils"
|
||||
|
||||
SOURCE_FILES = glob(
|
||||
[
|
||||
"src/**/*.ts",
|
||||
],
|
||||
exclude = [
|
||||
"**/*.test.*",
|
||||
"**/*.mock.*",
|
||||
],
|
||||
)
|
||||
|
||||
SRCS = SOURCE_FILES
|
||||
|
||||
filegroup(
|
||||
name = "srcs",
|
||||
srcs = SRCS,
|
||||
)
|
||||
|
||||
NPM_MODULE_EXTRA_FILES = [
|
||||
"package.json",
|
||||
"README.md",
|
||||
]
|
||||
|
||||
SRC_DEPS = [
|
||||
"@npm//react",
|
||||
"@npm//rxjs",
|
||||
"@npm//tslib",
|
||||
]
|
||||
|
||||
TYPES_DEPS = [
|
||||
"@npm//@types/jest",
|
||||
"@npm//@types/node",
|
||||
"@npm//@types/react",
|
||||
]
|
||||
|
||||
DEPS = SRC_DEPS + TYPES_DEPS
|
||||
|
||||
ts_config(
|
||||
name = "tsconfig",
|
||||
src = "tsconfig.json",
|
||||
deps = [
|
||||
"//:tsconfig.base.json",
|
||||
],
|
||||
)
|
||||
|
||||
ts_project(
|
||||
name = "tsc",
|
||||
srcs = SRCS,
|
||||
args = ["--pretty"],
|
||||
declaration = True,
|
||||
declaration_map = True,
|
||||
incremental = True,
|
||||
out_dir = "target",
|
||||
root_dir = "src",
|
||||
source_map = True,
|
||||
tsconfig = ":tsconfig",
|
||||
deps = DEPS,
|
||||
)
|
||||
|
||||
js_library(
|
||||
name = PKG_BASE_NAME,
|
||||
package_name = PKG_REQUIRE_NAME,
|
||||
srcs = NPM_MODULE_EXTRA_FILES,
|
||||
visibility = ["//visibility:public"],
|
||||
deps = DEPS + [":tsc"],
|
||||
)
|
||||
|
||||
pkg_npm(
|
||||
name = "npm_module",
|
||||
deps = [
|
||||
":%s" % PKG_BASE_NAME,
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "build",
|
||||
srcs = [
|
||||
":npm_module",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
3
packages/kbn-securitysolution-hook-utils/README.md
Normal file
3
packages/kbn-securitysolution-hook-utils/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# kbn-securitysolution-hook-utils
|
||||
|
||||
This package contains shared utilities for React hooks.
|
13
packages/kbn-securitysolution-hook-utils/jest.config.js
Normal file
13
packages/kbn-securitysolution-hook-utils/jest.config.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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 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 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../..',
|
||||
roots: ['<rootDir>/packages/kbn-securitysolution-hook-utils'],
|
||||
};
|
9
packages/kbn-securitysolution-hook-utils/package.json
Normal file
9
packages/kbn-securitysolution-hook-utils/package.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "@kbn/securitysolution-hook-utils",
|
||||
"version": "1.0.0",
|
||||
"description": "Security Solution utilities for React hooks",
|
||||
"license": "SSPL-1.0 OR Elastic License 2.0",
|
||||
"main": "./target/index.js",
|
||||
"types": "./target/index.d.ts",
|
||||
"private": true
|
||||
}
|
12
packages/kbn-securitysolution-hook-utils/src/index.ts
Normal file
12
packages/kbn-securitysolution-hook-utils/src/index.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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 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 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export * from './use_async';
|
||||
export * from './use_is_mounted';
|
||||
export * from './use_observable';
|
||||
export * from './with_optional_signal';
|
18
packages/kbn-securitysolution-hook-utils/src/types.ts
Normal file
18
packages/kbn-securitysolution-hook-utils/src/types.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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 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 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the state of an asynchronous task, along with an initiator
|
||||
* function to kick off the work.
|
||||
*/
|
||||
export interface Task<Args extends unknown[], Result> {
|
||||
loading: boolean;
|
||||
error: unknown | undefined;
|
||||
result: Result | undefined;
|
||||
start: (...args: Args) => void;
|
||||
}
|
|
@ -8,26 +8,26 @@
|
|||
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import { Task } from '../types';
|
||||
import { useIsMounted } from '../use_is_mounted';
|
||||
|
||||
// TODO: This is probably better off in another package such as kbn-securitysolution-hook-utils
|
||||
|
||||
export interface Async<Args extends unknown[], Result> {
|
||||
loading: boolean;
|
||||
error: unknown | undefined;
|
||||
result: Result | undefined;
|
||||
start: (...args: Args) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fn Async function
|
||||
* This hook wraps a promise-returning thunk (task) in order to conditionally
|
||||
* initiate the work, and automatically provide state corresponding to the
|
||||
* task's status.
|
||||
*
|
||||
* @returns An {@link AsyncTask} containing the underlying task's state along with a start callback
|
||||
* In order to function properly and not rerender unnecessarily, ensure that
|
||||
* your task is a stable function reference.
|
||||
*
|
||||
* @param fn a function returning a promise.
|
||||
*
|
||||
* @returns An {@link Task} containing the task's current state along with a
|
||||
* start callback
|
||||
*/
|
||||
export const useAsync = <Args extends unknown[], Result>(
|
||||
fn: (...args: Args) => Promise<Result>
|
||||
): Async<Args, Result> => {
|
||||
): Task<Args, Result> => {
|
||||
const isMounted = useIsMounted();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<unknown | undefined>();
|
|
@ -10,8 +10,6 @@ import { useCallback, useEffect, useRef } from 'react';
|
|||
|
||||
type GetIsMounted = () => boolean;
|
||||
|
||||
// TODO: This is probably better off in another package such as kbn-securitysolution-hook-utils
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns A {@link GetIsMounted} getter function returning whether the component is currently mounted
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* 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 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 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import { Subject, throwError } from 'rxjs';
|
||||
|
||||
import { useObservable } from '.';
|
||||
|
||||
interface TestArgs {
|
||||
n: number;
|
||||
s: string;
|
||||
}
|
||||
|
||||
type TestReturn = Subject<unknown>;
|
||||
|
||||
describe('useObservable', () => {
|
||||
let fn: jest.Mock<TestReturn, TestArgs[]>;
|
||||
let subject: TestReturn;
|
||||
let args: TestArgs;
|
||||
|
||||
beforeEach(() => {
|
||||
args = { n: 1, s: 's' };
|
||||
subject = new Subject();
|
||||
fn = jest.fn().mockReturnValue(subject);
|
||||
});
|
||||
|
||||
it('does not invoke fn if start was not called', () => {
|
||||
renderHook(() => useObservable(fn));
|
||||
expect(fn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('invokes the function when start is called', () => {
|
||||
const { result } = renderHook(() => useObservable(fn));
|
||||
|
||||
act(() => {
|
||||
result.current.start(args);
|
||||
});
|
||||
|
||||
expect(fn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('invokes the function with start args', () => {
|
||||
const { result } = renderHook(() => useObservable(fn));
|
||||
const expectedArgs = { ...args };
|
||||
|
||||
act(() => {
|
||||
result.current.start(args);
|
||||
});
|
||||
|
||||
expect(fn).toHaveBeenCalledWith(expectedArgs);
|
||||
});
|
||||
|
||||
it('populates result with the next value of the fn', () => {
|
||||
const { result } = renderHook(() => useObservable(fn));
|
||||
|
||||
act(() => {
|
||||
result.current.start(args);
|
||||
});
|
||||
act(() => subject.next('value'));
|
||||
|
||||
expect(result.current.result).toEqual('value');
|
||||
expect(result.current.error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('populates error if observable throws an error', () => {
|
||||
const error = new Error('whoops');
|
||||
const errorFn = () => throwError(error);
|
||||
|
||||
const { result } = renderHook(() => useObservable(errorFn));
|
||||
|
||||
act(() => {
|
||||
result.current.start();
|
||||
});
|
||||
|
||||
expect(result.current.result).toBeUndefined();
|
||||
expect(result.current.error).toEqual(error);
|
||||
});
|
||||
|
||||
it('populates the loading state while no value has resolved', () => {
|
||||
const { result } = renderHook(() => useObservable(fn));
|
||||
|
||||
act(() => {
|
||||
result.current.start(args);
|
||||
});
|
||||
|
||||
expect(result.current.loading).toBe(true);
|
||||
|
||||
act(() => subject.next('a value'));
|
||||
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
it('updates result with each resolved value', () => {
|
||||
const { result } = renderHook(() => useObservable(fn));
|
||||
|
||||
act(() => {
|
||||
result.current.start(args);
|
||||
});
|
||||
|
||||
act(() => subject.next('a value'));
|
||||
expect(result.current.result).toEqual('a value');
|
||||
|
||||
act(() => subject.next('a subsequent value'));
|
||||
expect(result.current.result).toEqual('a subsequent value');
|
||||
});
|
||||
|
||||
it('does not update result with values if start has not been called', () => {
|
||||
const { result } = renderHook(() => useObservable(fn));
|
||||
|
||||
act(() => subject.next('a value'));
|
||||
expect(result.current.result).toBeUndefined();
|
||||
|
||||
act(() => subject.next('a subsequent value'));
|
||||
expect(result.current.result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('unsubscribes on unmount', () => {
|
||||
const { result, unmount } = renderHook(() => useObservable(fn));
|
||||
|
||||
act(() => {
|
||||
result.current.start(args);
|
||||
});
|
||||
expect(subject.observers).toHaveLength(1);
|
||||
|
||||
unmount();
|
||||
expect(subject.observers).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('multiple start calls reset state', () => {
|
||||
const { result } = renderHook(() => useObservable(fn));
|
||||
|
||||
act(() => {
|
||||
result.current.start(args);
|
||||
});
|
||||
|
||||
expect(result.current.loading).toBe(true);
|
||||
|
||||
act(() => subject.next('one value'));
|
||||
|
||||
expect(result.current.loading).toBe(false);
|
||||
expect(result.current.result).toBe('one value');
|
||||
|
||||
act(() => {
|
||||
result.current.start(args);
|
||||
});
|
||||
|
||||
expect(result.current.loading).toBe(true);
|
||||
expect(result.current.result).toBe(undefined);
|
||||
|
||||
act(() => subject.next('another value'));
|
||||
|
||||
expect(result.current.loading).toBe(false);
|
||||
expect(result.current.result).toBe('another value');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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 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 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
|
||||
import { useIsMounted } from '../use_is_mounted';
|
||||
import { Task } from '../types';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fn function returning an observable
|
||||
*
|
||||
* @returns An {@link Async} containing the underlying task's state along with a start callback
|
||||
*/
|
||||
export const useObservable = <Args extends unknown[], Result>(
|
||||
fn: (...args: Args) => Observable<Result>
|
||||
): Task<Args, Result> => {
|
||||
const isMounted = useIsMounted();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<unknown | undefined>();
|
||||
const [result, setResult] = useState<Result | undefined>();
|
||||
const subRef = useRef<Subscription | undefined>();
|
||||
|
||||
const start = useCallback(
|
||||
(...args: Args) => {
|
||||
if (subRef.current) {
|
||||
subRef.current.unsubscribe();
|
||||
}
|
||||
setLoading(true);
|
||||
setResult(undefined);
|
||||
setError(undefined);
|
||||
|
||||
subRef.current = fn(...args).subscribe(
|
||||
(r) => {
|
||||
if (isMounted()) {
|
||||
setResult(r);
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
(e) => {
|
||||
if (isMounted()) {
|
||||
setError(e);
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
[fn, isMounted]
|
||||
);
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
if (subRef.current) {
|
||||
subRef.current.unsubscribe();
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return {
|
||||
error,
|
||||
loading,
|
||||
result,
|
||||
start,
|
||||
};
|
||||
};
|
|
@ -12,8 +12,6 @@ interface SignalArgs {
|
|||
|
||||
export type OptionalSignalArgs<Args> = Omit<Args, 'signal'> & Partial<SignalArgs>;
|
||||
|
||||
// TODO: This is probably better off in another package such as kbn-securitysolution-hook-utils
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fn an async function receiving an AbortSignal argument
|
14
packages/kbn-securitysolution-hook-utils/tsconfig.json
Normal file
14
packages/kbn-securitysolution-hook-utils/tsconfig.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"incremental": true,
|
||||
"outDir": "target",
|
||||
"rootDir": "src",
|
||||
"sourceMap": true,
|
||||
"sourceRoot": "../../../../packages/kbn-securitysolution-hook-utils/src",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
|
@ -28,6 +28,7 @@ NPM_MODULE_EXTRA_FILES = [
|
|||
]
|
||||
|
||||
SRC_DEPS = [
|
||||
"//packages/kbn-securitysolution-hook-utils",
|
||||
"//packages/kbn-securitysolution-io-ts-list-types",
|
||||
"//packages/kbn-securitysolution-list-api",
|
||||
"//packages/kbn-securitysolution-list-constants",
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
*/
|
||||
export * from './transforms';
|
||||
export * from './use_api';
|
||||
export * from './use_async';
|
||||
export * from './use_create_list_index';
|
||||
export * from './use_cursor';
|
||||
export * from './use_delete_list';
|
||||
|
@ -16,9 +15,7 @@ export * from './use_exception_lists';
|
|||
export * from './use_export_list';
|
||||
export * from './use_find_lists';
|
||||
export * from './use_import_list';
|
||||
export * from './use_is_mounted';
|
||||
export * from './use_persist_exception_item';
|
||||
export * from './use_persist_exception_list';
|
||||
export * from './use_read_list_index';
|
||||
export * from './use_read_list_privileges';
|
||||
export * from './with_optional_signal';
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
*/
|
||||
|
||||
import { createListIndex } from '@kbn/securitysolution-list-api';
|
||||
import { withOptionalSignal } from '../with_optional_signal';
|
||||
import { useAsync } from '../use_async';
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils';
|
||||
|
||||
const createListIndexWithOptionalSignal = withOptionalSignal(createListIndex);
|
||||
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
*/
|
||||
|
||||
import { deleteList } from '@kbn/securitysolution-list-api';
|
||||
import { withOptionalSignal } from '../with_optional_signal';
|
||||
import { useAsync } from '../use_async';
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils';
|
||||
|
||||
const deleteListWithOptionalSignal = withOptionalSignal(deleteList);
|
||||
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
*/
|
||||
|
||||
import { exportList } from '@kbn/securitysolution-list-api';
|
||||
import { withOptionalSignal } from '../with_optional_signal';
|
||||
import { useAsync } from '../use_async';
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils';
|
||||
|
||||
const exportListWithOptionalSignal = withOptionalSignal(exportList);
|
||||
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
*/
|
||||
|
||||
import { findLists } from '@kbn/securitysolution-list-api';
|
||||
import { withOptionalSignal } from '../with_optional_signal';
|
||||
import { useAsync } from '../use_async';
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils';
|
||||
|
||||
const findListsWithOptionalSignal = withOptionalSignal(findLists);
|
||||
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
*/
|
||||
|
||||
import { importList } from '@kbn/securitysolution-list-api';
|
||||
import { withOptionalSignal } from '../with_optional_signal';
|
||||
import { useAsync } from '../use_async';
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils';
|
||||
|
||||
const importListWithOptionalSignal = withOptionalSignal(importList);
|
||||
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
*/
|
||||
|
||||
import { readListIndex } from '@kbn/securitysolution-list-api';
|
||||
import { withOptionalSignal } from '../with_optional_signal';
|
||||
import { useAsync } from '../use_async';
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils';
|
||||
|
||||
const readListIndexWithOptionalSignal = withOptionalSignal(readListIndex);
|
||||
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
*/
|
||||
|
||||
import { readListPrivileges } from '@kbn/securitysolution-list-api';
|
||||
import { withOptionalSignal } from '../with_optional_signal';
|
||||
import { useAsync } from '../use_async';
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils';
|
||||
|
||||
const readListPrivilegesWithOptionalSignal = withOptionalSignal(readListPrivileges);
|
||||
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { withOptionalSignal } from './with_optional_signal';
|
||||
|
||||
type TestFn = ({ number, signal }: { number: number; signal: AbortSignal }) => boolean;
|
||||
|
||||
describe('withOptionalSignal', () => {
|
||||
it('does not require a signal on the returned function', () => {
|
||||
const fn = jest.fn().mockReturnValue('hello') as TestFn;
|
||||
|
||||
const wrappedFn = withOptionalSignal(fn);
|
||||
|
||||
expect(wrappedFn({ number: 1 })).toEqual('hello');
|
||||
});
|
||||
|
||||
it('will pass a given signal to the wrapped function', () => {
|
||||
const fn = jest.fn().mockReturnValue('hello') as TestFn;
|
||||
const { signal } = new AbortController();
|
||||
|
||||
const wrappedFn = withOptionalSignal(fn);
|
||||
|
||||
wrappedFn({ number: 1, signal });
|
||||
expect(fn).toHaveBeenCalledWith({ number: 1, signal });
|
||||
});
|
||||
});
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
interface SignalArgs {
|
||||
signal: AbortSignal;
|
||||
}
|
||||
|
||||
export type OptionalSignalArgs<Args> = Omit<Args, 'signal'> & Partial<SignalArgs>;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fn an async function receiving an AbortSignal argument
|
||||
*
|
||||
* @returns An async function where the AbortSignal argument is optional
|
||||
*/
|
||||
export const withOptionalSignal = <Args extends SignalArgs, Result>(fn: (args: Args) => Result) => (
|
||||
args: OptionalSignalArgs<Args>
|
||||
): Result => {
|
||||
const signal = args.signal ?? new AbortController().signal;
|
||||
return fn({ ...args, signal } as Args);
|
||||
};
|
|
@ -13,7 +13,7 @@ import { waitFor } from '@testing-library/react';
|
|||
import { AddExceptionModal } from './';
|
||||
import { useCurrentUser } from '../../../../common/lib/kibana';
|
||||
import { ExceptionBuilder } from '../../../../shared_imports';
|
||||
import { useAsync } from '@kbn/securitysolution-list-hooks';
|
||||
import { useAsync } from '@kbn/securitysolution-hook-utils';
|
||||
import { getExceptionListSchemaMock } from '../../../../../../lists/common/schemas/response/exception_list_schema.mock';
|
||||
import { useFetchIndex } from '../../../containers/source';
|
||||
import { stubIndexPattern } from 'src/plugins/data/common/index_patterns/index_pattern.stub';
|
||||
|
@ -49,8 +49,8 @@ jest.mock('../../../containers/source');
|
|||
jest.mock('../../../../detections/containers/detection_engine/rules');
|
||||
jest.mock('../use_add_exception');
|
||||
jest.mock('../use_fetch_or_create_rule_exception_list');
|
||||
jest.mock('@kbn/securitysolution-list-hooks', () => ({
|
||||
...jest.requireActual('@kbn/securitysolution-list-hooks'),
|
||||
jest.mock('@kbn/securitysolution-hook-utils', () => ({
|
||||
...jest.requireActual('@kbn/securitysolution-hook-utils'),
|
||||
useAsync: jest.fn(),
|
||||
}));
|
||||
jest.mock('../../../../detections/containers/detection_engine/rules/use_rule_async');
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-list-hooks';
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils';
|
||||
import { getJobs } from '../api/get_jobs';
|
||||
import { CombinedJobWithStats } from '../../../../../../ml/common/types/anomaly_detection_jobs';
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-list-hooks';
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils';
|
||||
import { getJobsSummary } from '../api/get_jobs_summary';
|
||||
|
||||
const _getJobsSummary = withOptionalSignal(getJobsSummary);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-list-hooks';
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils';
|
||||
import { getMlCapabilities } from '../api/get_ml_capabilities';
|
||||
|
||||
const _getMlCapabilities = withOptionalSignal(getMlCapabilities);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-list-hooks';
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils';
|
||||
import { useAppToasts } from '../../../common/hooks/use_app_toasts';
|
||||
import { getUserPrivilege } from '../../containers/detection_engine/alerts/api';
|
||||
import * as i18n from './translations';
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { useEffect, useCallback } from 'react';
|
||||
|
||||
import { flow } from 'fp-ts/lib/function';
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-list-hooks';
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils';
|
||||
import { useHttp } from '../../../../common/lib/kibana';
|
||||
import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
|
||||
import { pureFetchRuleById } from './api';
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-list-hooks';
|
||||
import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils';
|
||||
import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
|
||||
import { isNotFoundError } from '../../../../common/utils/api';
|
||||
import { useQueryAlerts } from '../alerts/use_query';
|
||||
|
|
|
@ -2716,6 +2716,10 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/securitysolution-hook-utils@link:bazel-bin/packages/kbn-securitysolution-hook-utils":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/securitysolution-io-ts-alerting-types@link:bazel-bin/packages/kbn-securitysolution-io-ts-alerting-types":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue