From 0bf745ed2e9a07b44b444e86026d444f04c10904 Mon Sep 17 00:00:00 2001 From: Spencer Date: Mon, 14 Mar 2022 16:46:47 -0600 Subject: [PATCH 01/10] [type-summarizer] build types with sourcemaps for @kbn/mapbox-gl (#127529) --- packages/kbn-type-summarizer/src/bazel_cli.ts | 70 ++++++++++--------- .../src/lib/bazel_cli_config.ts | 55 +++++++++------ .../lib/export_collector/collector_results.ts | 54 ++++++-------- .../lib/export_collector/exports_collector.ts | 31 ++++---- .../lib/export_collector/imported_symbol.ts | 68 ++++++++++++++++++ .../lib/export_collector/imported_symbols.ts | 21 ------ .../src/lib/export_collector/index.ts | 1 + .../src/lib/export_collector/result_value.ts | 3 +- .../src/lib/export_info.ts | 44 +++++++++++- .../kbn-type-summarizer/src/lib/printer.ts | 67 ++++++++++++++---- .../integration_tests/import_boundary.test.ts | 45 ++++++++++-- ...ummarizer_output.js => type_summarizer.js} | 4 +- 12 files changed, 317 insertions(+), 146 deletions(-) create mode 100644 packages/kbn-type-summarizer/src/lib/export_collector/imported_symbol.ts delete mode 100644 packages/kbn-type-summarizer/src/lib/export_collector/imported_symbols.ts rename scripts/{build_type_summarizer_output.js => type_summarizer.js} (76%) diff --git a/packages/kbn-type-summarizer/src/bazel_cli.ts b/packages/kbn-type-summarizer/src/bazel_cli.ts index af6b13ebfc09c..2a6b8eb245d4c 100644 --- a/packages/kbn-type-summarizer/src/bazel_cli.ts +++ b/packages/kbn-type-summarizer/src/bazel_cli.ts @@ -10,7 +10,7 @@ import Fsp from 'fs/promises'; import Path from 'path'; import { run } from './lib/run'; -import { parseBazelCliConfig } from './lib/bazel_cli_config'; +import { parseBazelCliConfigs } from './lib/bazel_cli_config'; import { summarizePackage } from './summarize_package'; import { runApiExtractor } from './run_api_extractor'; @@ -29,41 +29,43 @@ run( log.debug('cwd:', process.cwd()); log.debug('argv', process.argv); - const config = parseBazelCliConfig(argv); - await Fsp.mkdir(config.outputDir, { recursive: true }); + for (const config of parseBazelCliConfigs(argv)) { + await Fsp.rm(config.outputDir, { recursive: true }); + await Fsp.mkdir(config.outputDir, { recursive: true }); - // generate pkg json output - await Fsp.writeFile( - Path.resolve(config.outputDir, 'package.json'), - JSON.stringify( - { - name: `@types/${config.packageName.replaceAll('@', '').replaceAll('/', '__')}`, - description: 'Generated by @kbn/type-summarizer', - types: './index.d.ts', - private: true, - license: 'MIT', - version: '1.1.0', - }, - null, - 2 - ) - ); - - if (config.use === 'type-summarizer') { - await summarizePackage(log, { - dtsDir: Path.dirname(config.inputPath), - inputPaths: [config.inputPath], - outputDir: config.outputDir, - tsconfigPath: config.tsconfigPath, - repoRelativePackageDir: config.repoRelativePackageDir, - }); - log.success('type summary created for', config.repoRelativePackageDir); - } else { - await runApiExtractor( - config.tsconfigPath, - config.inputPath, - Path.resolve(config.outputDir, 'index.d.ts') + // generate pkg json output + await Fsp.writeFile( + Path.resolve(config.outputDir, 'package.json'), + JSON.stringify( + { + name: `@types/${config.packageName.replaceAll('@', '').replaceAll('/', '__')}`, + description: 'Generated by @kbn/type-summarizer', + types: './index.d.ts', + private: true, + license: 'MIT', + version: '1.1.0', + }, + null, + 2 + ) ); + + if (config.type === 'type-summarizer') { + await summarizePackage(log, { + dtsDir: Path.dirname(config.inputPath), + inputPaths: [config.inputPath], + outputDir: config.outputDir, + tsconfigPath: config.tsconfigPath, + repoRelativePackageDir: config.repoRelativePackageDir, + }); + log.success('type summary created for', config.repoRelativePackageDir); + } else { + await runApiExtractor( + config.tsconfigPath, + config.inputPath, + Path.resolve(config.outputDir, 'index.d.ts') + ); + } } }, { diff --git a/packages/kbn-type-summarizer/src/lib/bazel_cli_config.ts b/packages/kbn-type-summarizer/src/lib/bazel_cli_config.ts index da82aef8f7d79..28d7063df6d54 100644 --- a/packages/kbn-type-summarizer/src/lib/bazel_cli_config.ts +++ b/packages/kbn-type-summarizer/src/lib/bazel_cli_config.ts @@ -12,7 +12,17 @@ import { CliError } from './cli_error'; import { parseCliFlags } from './cli_flags'; import * as Path from './path'; -const TYPE_SUMMARIZER_PACKAGES = ['@kbn/type-summarizer', '@kbn/crypto', '@kbn/generate']; +const TYPE_SUMMARIZER_PACKAGES = [ + '@kbn/type-summarizer', + '@kbn/crypto', + '@kbn/generate', + '@kbn/mapbox-gl', +]; + +type TypeSummarizerType = 'api-extractor' | 'type-summarizer'; +function isTypeSummarizerType(v: string): v is TypeSummarizerType { + return v === 'api-extractor' || v === 'type-summarizer'; +} const isString = (i: any): i is string => typeof i === 'string' && i.length > 0; @@ -22,7 +32,7 @@ interface BazelCliConfig { tsconfigPath: string; inputPath: string; repoRelativePackageDir: string; - use: 'api-extractor' | 'type-summarizer'; + type: TypeSummarizerType; } function isKibanaRepo(dir: string) { @@ -55,11 +65,11 @@ function findRepoRoot() { } } -export function parseBazelCliFlags(argv: string[]): BazelCliConfig { +export function parseBazelCliFlags(argv: string[]): BazelCliConfig[] { const { rawFlags, unknownFlags } = parseCliFlags(argv, { string: ['use'], default: { - use: 'api-extractor', + use: 'api-extractor,type-summarizer', }, }); @@ -79,8 +89,11 @@ export function parseBazelCliFlags(argv: string[]): BazelCliConfig { throw new CliError(`extra positional arguments`, { showHelp: true }); } - const use = rawFlags.use; - if (use !== 'api-extractor' && use !== 'type-summarizer') { + const use = String(rawFlags.use || '') + .split(',') + .map((t) => t.trim()) + .filter(Boolean); + if (!use.every(isTypeSummarizerType)) { throw new CliError(`invalid --use flag, expected "api-extractor" or "type-summarizer"`); } @@ -90,14 +103,14 @@ export function parseBazelCliFlags(argv: string[]): BazelCliConfig { ).name; const repoRelativePackageDir = Path.relative(repoRoot, packageDir); - return { - use, + return use.map((type) => ({ + type, packageName, tsconfigPath: Path.join(repoRoot, repoRelativePackageDir, 'tsconfig.json'), inputPath: Path.join(repoRoot, 'node_modules', packageName, 'target_types/index.d.ts'), repoRelativePackageDir, - outputDir: Path.join(repoRoot, 'data/type-summarizer-output', use), - }; + outputDir: Path.join(repoRoot, 'data/type-summarizer-output', type), + })); } function parseJsonFromCli(json: string) { @@ -125,7 +138,7 @@ function parseJsonFromCli(json: string) { } } -export function parseBazelCliJson(json: string): BazelCliConfig { +export function parseBazelCliJson(json: string): BazelCliConfig[] { const config = parseJsonFromCli(json); if (typeof config !== 'object' || config === null) { throw new CliError('config JSON must be an object'); @@ -168,17 +181,19 @@ export function parseBazelCliJson(json: string): BazelCliConfig { throw new CliError(`buildFilePath [${buildFilePath}] must be a relative path`); } - return { - packageName, - outputDir: Path.resolve(outputDir), - tsconfigPath: Path.resolve(tsconfigPath), - inputPath: Path.resolve(inputPath), - repoRelativePackageDir: Path.dirname(buildFilePath), - use: TYPE_SUMMARIZER_PACKAGES.includes(packageName) ? 'type-summarizer' : 'api-extractor', - }; + return [ + { + packageName, + outputDir: Path.resolve(outputDir), + tsconfigPath: Path.resolve(tsconfigPath), + inputPath: Path.resolve(inputPath), + repoRelativePackageDir: Path.dirname(buildFilePath), + type: TYPE_SUMMARIZER_PACKAGES.includes(packageName) ? 'type-summarizer' : 'api-extractor', + }, + ]; } -export function parseBazelCliConfig(argv: string[]) { +export function parseBazelCliConfigs(argv: string[]): BazelCliConfig[] { if (typeof argv[0] === 'string' && argv[0].startsWith('{')) { return parseBazelCliJson(argv[0]); } diff --git a/packages/kbn-type-summarizer/src/lib/export_collector/collector_results.ts b/packages/kbn-type-summarizer/src/lib/export_collector/collector_results.ts index f8f4e131f8386..6b1f98a10f69a 100644 --- a/packages/kbn-type-summarizer/src/lib/export_collector/collector_results.ts +++ b/packages/kbn-type-summarizer/src/lib/export_collector/collector_results.ts @@ -7,58 +7,46 @@ */ import * as ts from 'typescript'; -import { ValueNode, ExportFromDeclaration } from '../ts_nodes'; +import { ValueNode, DecSymbol } from '../ts_nodes'; import { ResultValue } from './result_value'; -import { ImportedSymbols } from './imported_symbols'; +import { ImportedSymbol } from './imported_symbol'; import { Reference, ReferenceKey } from './reference'; import { SourceMapper } from '../source_mapper'; +import { ExportInfo } from '../export_info'; -export type CollectorResult = Reference | ImportedSymbols | ResultValue; +export type CollectorResult = + | Reference + | { type: 'imports'; imports: ImportedSymbol[] } + | ResultValue; export class CollectorResults { - imports: ImportedSymbols[] = []; - importsByPath = new Map(); + importedSymbols = new Set(); nodes: ResultValue[] = []; nodesByAst = new Map(); constructor(private readonly sourceMapper: SourceMapper) {} - addNode(exported: boolean, node: ValueNode) { + addNode(exportInfo: ExportInfo | undefined, node: ValueNode) { const existing = this.nodesByAst.get(node); if (existing) { - existing.exported = existing.exported || exported; + existing.exportInfo ||= exportInfo; return; } - const result = new ResultValue(exported, node); + const result = new ResultValue(exportInfo, node); this.nodesByAst.set(node, result); this.nodes.push(result); } - ensureExported(node: ValueNode) { - this.addNode(true, node); - } - - addImport( - exported: boolean, - node: ts.ImportDeclaration | ExportFromDeclaration, - symbol: ts.Symbol + addImportFromNodeModules( + exportInfo: ExportInfo | undefined, + symbol: DecSymbol, + moduleId: string ) { - const literal = node.moduleSpecifier; - if (!ts.isStringLiteral(literal)) { - throw new Error('import statement with non string literal module identifier'); - } - - const existing = this.importsByPath.get(literal.text); - if (existing) { - existing.symbols.push(symbol); - return; - } - - const result = new ImportedSymbols(exported, node, [symbol]); - this.importsByPath.set(literal.text, result); - this.imports.push(result); + const imp = ImportedSymbol.fromSymbol(symbol, moduleId); + imp.exportInfo ||= exportInfo; + this.importedSymbols.add(imp); } private getReferencesFromNodes() { @@ -88,6 +76,10 @@ export class CollectorResults { } getAll(): CollectorResult[] { - return [...this.getReferencesFromNodes(), ...this.imports, ...this.nodes]; + return [ + ...this.getReferencesFromNodes(), + { type: 'imports', imports: Array.from(this.importedSymbols) }, + ...this.nodes, + ]; } } diff --git a/packages/kbn-type-summarizer/src/lib/export_collector/exports_collector.ts b/packages/kbn-type-summarizer/src/lib/export_collector/exports_collector.ts index 3f46ceda70e1f..b6e7a8382bc0a 100644 --- a/packages/kbn-type-summarizer/src/lib/export_collector/exports_collector.ts +++ b/packages/kbn-type-summarizer/src/lib/export_collector/exports_collector.ts @@ -26,9 +26,9 @@ import { SourceMapper } from '../source_mapper'; import { isNodeModule } from '../is_node_module'; interface ResolvedNmImport { - type: 'import'; - node: ts.ImportDeclaration | ExportFromDeclaration; - targetPath: string; + type: 'import_from_node_modules'; + symbol: DecSymbol; + moduleId: string; } interface ResolvedSymbol { type: 'symbol'; @@ -93,15 +93,14 @@ export class ExportCollector { private getImportFromNodeModules(symbol: DecSymbol): undefined | ResolvedNmImport { const parentImport = this.getParentImport(symbol); - if (parentImport) { - // this symbol is within an import statement, is it an import from a node_module? + if (parentImport && ts.isStringLiteral(parentImport.moduleSpecifier)) { + // this symbol is within an import statement, is it an import from a node_module? first + // we resolve the import alias to it's source symbol, which we will show us the file that + // the import resolves to const aliased = this.resolveAliasSymbolStep(symbol); - - // symbol is in an import or export-from statement, make sure we want to traverse to that file const targetPaths = [ ...new Set(aliased.declarations.map((d) => this.sourceMapper.getSourceFile(d).fileName)), ]; - if (targetPaths.length > 1) { throw new Error('importing a symbol from multiple locations is unsupported at this time'); } @@ -109,9 +108,9 @@ export class ExportCollector { const targetPath = targetPaths[0]; if (isNodeModule(this.dtsDir, targetPath)) { return { - type: 'import', - node: parentImport, - targetPath, + type: 'import_from_node_modules', + symbol, + moduleId: parentImport.moduleSpecifier.text, }; } } @@ -148,8 +147,8 @@ export class ExportCollector { this.traversedSymbols.add(symbol); const source = this.resolveAliasSymbol(symbol); - if (source.type === 'import') { - results.addImport(!!exportInfo, source.node, symbol); + if (source.type === 'import_from_node_modules') { + results.addImportFromNodeModules(exportInfo, source.symbol, source.moduleId); return; } @@ -157,7 +156,7 @@ export class ExportCollector { if (seen) { for (const node of symbol.declarations) { assertExportedValueNode(node); - results.ensureExported(node); + results.addNode(exportInfo, node); } return; } @@ -185,7 +184,7 @@ export class ExportCollector { } if (isExportedValueNode(node)) { - results.addNode(!!exportInfo, node); + results.addNode(exportInfo, node); } } } @@ -201,7 +200,7 @@ export class ExportCollector { for (const symbol of this.typeChecker.getExportsOfModule(moduleSymbol)) { assertDecSymbol(symbol); - this.collectResults(results, new ExportInfo(`${symbol.escapedName}`), symbol); + this.collectResults(results, ExportInfo.fromSymbol(symbol), symbol); } return results; diff --git a/packages/kbn-type-summarizer/src/lib/export_collector/imported_symbol.ts b/packages/kbn-type-summarizer/src/lib/export_collector/imported_symbol.ts new file mode 100644 index 0000000000000..04ddd7730df0c --- /dev/null +++ b/packages/kbn-type-summarizer/src/lib/export_collector/imported_symbol.ts @@ -0,0 +1,68 @@ +/* + * 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 * as ts from 'typescript'; +import { DecSymbol, findKind } from '../ts_nodes'; +import { ExportInfo } from '../export_info'; + +const cache = new WeakMap(); + +export class ImportedSymbol { + static fromSymbol(symbol: DecSymbol, moduleId: string) { + const cached = cache.get(symbol); + if (cached) { + return cached; + } + + if (symbol.declarations.length !== 1) { + throw new Error('expected import symbol to have exactly one declaration'); + } + + const dec = symbol.declarations[0]; + if ( + !ts.isImportClause(dec) && + !ts.isExportSpecifier(dec) && + !ts.isImportSpecifier(dec) && + !ts.isNamespaceImport(dec) + ) { + const kind = findKind(dec); + throw new Error( + `expected import declaration to be an ImportClause, ImportSpecifier, or NamespaceImport, got ${kind}` + ); + } + + if (!dec.name) { + throw new Error(`expected ${findKind(dec)} to have a name defined`); + } + + const imp = ts.isImportClause(dec) + ? new ImportedSymbol(symbol, 'default', dec.name.text, dec.isTypeOnly, moduleId) + : ts.isNamespaceImport(dec) + ? new ImportedSymbol(symbol, '*', dec.name.text, dec.parent.isTypeOnly, moduleId) + : new ImportedSymbol( + symbol, + dec.name.text, + dec.propertyName?.text, + dec.isTypeOnly || dec.parent.parent.isTypeOnly, + moduleId + ); + + cache.set(symbol, imp); + return imp; + } + + public exportInfo: ExportInfo | undefined; + + constructor( + public readonly symbol: DecSymbol, + public readonly remoteName: string, + public readonly localName: string | undefined, + public readonly isTypeOnly: boolean, + public readonly moduleId: string + ) {} +} diff --git a/packages/kbn-type-summarizer/src/lib/export_collector/imported_symbols.ts b/packages/kbn-type-summarizer/src/lib/export_collector/imported_symbols.ts deleted file mode 100644 index 1c9fa800baaab..0000000000000 --- a/packages/kbn-type-summarizer/src/lib/export_collector/imported_symbols.ts +++ /dev/null @@ -1,21 +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 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 * as ts from 'typescript'; -import { ExportFromDeclaration } from '../ts_nodes'; - -export class ImportedSymbols { - type = 'import' as const; - - constructor( - public readonly exported: boolean, - public readonly importNode: ts.ImportDeclaration | ExportFromDeclaration, - // TODO: I'm going to need to keep track of local names for these... unless that's embedded in the symbols - public readonly symbols: ts.Symbol[] - ) {} -} diff --git a/packages/kbn-type-summarizer/src/lib/export_collector/index.ts b/packages/kbn-type-summarizer/src/lib/export_collector/index.ts index 87f6630d2fcfa..a0447445ac20f 100644 --- a/packages/kbn-type-summarizer/src/lib/export_collector/index.ts +++ b/packages/kbn-type-summarizer/src/lib/export_collector/index.ts @@ -8,3 +8,4 @@ export * from './exports_collector'; export * from './collector_results'; +export * from './imported_symbol'; diff --git a/packages/kbn-type-summarizer/src/lib/export_collector/result_value.ts b/packages/kbn-type-summarizer/src/lib/export_collector/result_value.ts index 91249eea68e14..839f5a6f99f82 100644 --- a/packages/kbn-type-summarizer/src/lib/export_collector/result_value.ts +++ b/packages/kbn-type-summarizer/src/lib/export_collector/result_value.ts @@ -7,9 +7,10 @@ */ import { ValueNode } from '../ts_nodes'; +import { ExportInfo } from '../export_info'; export class ResultValue { type = 'value' as const; - constructor(public exported: boolean, public readonly node: ValueNode) {} + constructor(public exportInfo: ExportInfo | undefined, public readonly node: ValueNode) {} } diff --git a/packages/kbn-type-summarizer/src/lib/export_info.ts b/packages/kbn-type-summarizer/src/lib/export_info.ts index 3dee04121d322..121f9345abde0 100644 --- a/packages/kbn-type-summarizer/src/lib/export_info.ts +++ b/packages/kbn-type-summarizer/src/lib/export_info.ts @@ -6,6 +6,48 @@ * Side Public License, v 1. */ +import * as ts from 'typescript'; +import { DecSymbol } from './ts_nodes'; + +type ExportType = 'export' | 'export type'; + export class ExportInfo { - constructor(public readonly name: string) {} + static fromSymbol(symbol: DecSymbol) { + const exportInfo = symbol.declarations.reduce((acc: ExportInfo | undefined, dec) => { + const next = ExportInfo.fromNode(dec, symbol); + if (!acc) { + return next; + } + + if (next.name !== acc.name || next.type !== acc.type) { + throw new Error('unable to handle export symbol with different types of declarations'); + } + + return acc; + }, undefined); + + if (!exportInfo) { + throw new Error('unable to get candidates'); + } + + return exportInfo; + } + + static fromNode(node: ts.Node, symbol: DecSymbol) { + let type: ExportType = 'export'; + + if (ts.isExportSpecifier(node)) { + if (node.isTypeOnly || node.parent.parent.isTypeOnly) { + type = 'export type'; + } + } + + return new ExportInfo(node.getText(), type, symbol); + } + + constructor( + public readonly name: string, + public readonly type: ExportType, + public readonly symbol: DecSymbol + ) {} } diff --git a/packages/kbn-type-summarizer/src/lib/printer.ts b/packages/kbn-type-summarizer/src/lib/printer.ts index 8ecc4356ea4a2..15a7a03363c02 100644 --- a/packages/kbn-type-summarizer/src/lib/printer.ts +++ b/packages/kbn-type-summarizer/src/lib/printer.ts @@ -12,7 +12,8 @@ import { SourceNode, CodeWithSourceMap } from 'source-map'; import * as Path from './path'; import { findKind } from './ts_nodes'; import { SourceMapper } from './source_mapper'; -import { CollectorResult } from './export_collector'; +import { ExportInfo } from './export_info'; +import { CollectorResult, ImportedSymbol } from './export_collector'; type SourceNodes = Array; const COMMENT_TRIM = /^(\s+)(\/\*|\*|\/\/)/; @@ -44,14 +45,11 @@ export class Printer { return `/// \n`; } - if (r.type === 'import') { - // TODO: handle default imports, imports with alternate names, etc - return `import { ${r.symbols - .map((s) => s.escapedName) - .join(', ')} } from ${r.importNode.moduleSpecifier.getText()};\n`; + if (r.type === 'imports') { + return this.printImports(r.imports); } - return this.toSourceNodes(r.node, r.exported); + return this.toSourceNodes(r.node, r.exportInfo); }) ); @@ -70,6 +68,45 @@ export class Printer { return output; } + private printImports(imports: ImportedSymbol[]) { + const importLines: string[] = []; + const exportLines: string[] = []; + + for (const imp of imports) { + const from = ` from '${imp.moduleId}';`; + + if (imp.remoteName === 'default') { + importLines.push( + [imp.isTypeOnly ? `import type ` : 'import ', imp.localName, from].join('') + ); + } else if (imp.remoteName === '*') { + importLines.push( + [imp.isTypeOnly ? `import type * as ` : 'import * as ', imp.localName, from].join('') + ); + } else { + importLines.push( + [ + imp.isTypeOnly ? 'import type { ' : 'import { ', + imp.localName ? `${imp.remoteName} as ${imp.localName}` : imp.remoteName, + ' }', + from, + ].join('') + ); + } + + if (imp.exportInfo) { + exportLines.push(`${imp.exportInfo.type} { ${imp.localName || imp.remoteName} };`); + } + } + + const lines = [ + ...importLines, + ...(importLines.length && exportLines.length ? [''] : []), + ...exportLines, + ]; + return lines.length ? lines.join('\n') + '\n\n' : ''; + } + private getDeclarationKeyword(node: ts.Declaration) { if (node.kind === ts.SyntaxKind.FunctionDeclaration) { return 'function'; @@ -92,11 +129,11 @@ export class Printer { } } - private printModifiers(exported: boolean, node: ts.Declaration) { + private printModifiers(exportInfo: ExportInfo | undefined, node: ts.Declaration) { const flags = ts.getCombinedModifierFlags(node); const modifiers: string[] = []; - if (exported) { - modifiers.push('export'); + if (exportInfo) { + modifiers.push(exportInfo.type); } if (flags & ts.ModifierFlags.Default) { modifiers.push('default'); @@ -248,7 +285,7 @@ export class Printer { return `<${typeParams.map((p) => this.printNode(p)).join(', ')}>`; } - private toSourceNodes(node: ts.Node, exported = false): SourceNodes { + private toSourceNodes(node: ts.Node, exportInfo?: ExportInfo): SourceNodes { switch (node.kind) { case ts.SyntaxKind.LiteralType: case ts.SyntaxKind.StringLiteral: @@ -267,7 +304,7 @@ export class Printer { return [ this.getLeadingComments(node), - this.printModifiers(exported, node), + this.printModifiers(exportInfo, node), this.getMappedSourceNode(node.name), this.printTypeParameters(node), `(${node.parameters.map((p) => p.getFullText()).join(', ')})`, @@ -294,7 +331,7 @@ export class Printer { if (ts.isVariableDeclaration(node)) { return [ ...this.getLeadingComments(node), - this.printModifiers(exported, node), + this.printModifiers(exportInfo, node), this.getMappedSourceNode(node.name), ...(node.type ? [': ', this.printNode(node.type)] : []), ';\n', @@ -310,7 +347,7 @@ export class Printer { if (ts.isTypeAliasDeclaration(node)) { return [ ...this.getLeadingComments(node), - this.printModifiers(exported, node), + this.printModifiers(exportInfo, node), this.getMappedSourceNode(node.name), this.printTypeParameters(node), ' = ', @@ -321,7 +358,7 @@ export class Printer { if (ts.isClassDeclaration(node)) { return [ ...this.getLeadingComments(node), - this.printModifiers(exported, node), + this.printModifiers(exportInfo, node), node.name ? this.getMappedSourceNode(node.name) : [], this.printTypeParameters(node), ' {\n', diff --git a/packages/kbn-type-summarizer/src/tests/integration_tests/import_boundary.test.ts b/packages/kbn-type-summarizer/src/tests/integration_tests/import_boundary.test.ts index f1e3279bb57b0..60f7d9489b00b 100644 --- a/packages/kbn-type-summarizer/src/tests/integration_tests/import_boundary.test.ts +++ b/packages/kbn-type-summarizer/src/tests/integration_tests/import_boundary.test.ts @@ -25,7 +25,7 @@ const nodeModules = { `, }; -it('output type links to named import from node modules', async () => { +it('output links to named import from node modules', async () => { const output = await run( ` import { Foo } from 'foo' @@ -36,13 +36,14 @@ it('output type links to named import from node modules', async () => { expect(output.code).toMatchInlineSnapshot(` "import { Foo } from 'foo'; + export type ValidName = string | Foo //# sourceMappingURL=index.d.ts.map" `); expect(output.map).toMatchInlineSnapshot(` Object { "file": "index.d.ts", - "mappings": ";YACY,S", + "mappings": ";;YACY,S", "names": Array [], "sourceRoot": "../../../src", "sources": Array [ @@ -57,7 +58,40 @@ it('output type links to named import from node modules', async () => { `); }); -it('output type links to default import from node modules', async () => { +it('output links to type exports from node modules', async () => { + const output = await run( + ` + export type { Foo } from 'foo' + `, + { + otherFiles: nodeModules, + } + ); + + expect(output.code).toMatchInlineSnapshot(` + "import type { Foo } from 'foo'; + + export type { Foo }; + + //# sourceMappingURL=index.d.ts.map" + `); + expect(output.map).toMatchInlineSnapshot(` + Object { + "file": "index.d.ts", + "mappings": "", + "names": Array [], + "sourceRoot": "../../../src", + "sources": Array [], + "version": 3, + } + `); + expect(output.logs).toMatchInlineSnapshot(` + "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/__tmp__/dist_dts/index.d.ts' ] + " + `); +}); + +it('output links to default import from node modules', async () => { const output = await run( ` import Bar from 'bar' @@ -67,14 +101,15 @@ it('output type links to default import from node modules', async () => { ); expect(output.code).toMatchInlineSnapshot(` - "import { Bar } from 'bar'; + "import Bar from 'bar'; + export type ValidName = string | Bar //# sourceMappingURL=index.d.ts.map" `); expect(output.map).toMatchInlineSnapshot(` Object { "file": "index.d.ts", - "mappings": ";YACY,S", + "mappings": ";;YACY,S", "names": Array [], "sourceRoot": "../../../src", "sources": Array [ diff --git a/scripts/build_type_summarizer_output.js b/scripts/type_summarizer.js similarity index 76% rename from scripts/build_type_summarizer_output.js rename to scripts/type_summarizer.js index 619c8db5d2d05..09d3aa69138e9 100644 --- a/scripts/build_type_summarizer_output.js +++ b/scripts/type_summarizer.js @@ -6,6 +6,6 @@ * Side Public License, v 1. */ -require('../src/setup_node_env/ensure_node_preserve_symlinks'); +require('../src/setup_node_env'); require('source-map-support/register'); -require('@kbn/type-summarizer/target_node/bazel_cli'); +require('../packages/kbn-type-summarizer/src/bazel_cli'); From 595b6948a1492a04fff14c8d6b2f8b50308681c0 Mon Sep 17 00:00:00 2001 From: Davey Holler Date: Mon, 14 Mar 2022 17:04:17 -0700 Subject: [PATCH 02/10] Adding a beta badge to the ES index card. (#127674) * Adding a beta badge to the ES index card. * i18n --- .../document_creation_buttons.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx index edc91804961e1..03f4c62a959cc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx @@ -69,7 +69,7 @@ export const DocumentCreationButtons: React.FC = ({

{i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.helperText', { defaultMessage: - 'There are four ways to send documents to your engine for indexing. You can paste or upload a JSON file, POST to the documents API endpoint, connect to an existing Elasticsearch index, or use the Elastic Web Crawler to automatically index documents from a URL.', + 'There are four ways to send documents to your engine for indexing. You can paste or upload a JSON file, POST to the documents API endpoint, connect to an existing Elasticsearch index (beta), or use the Elastic Web Crawler to automatically index documents from a URL.', })}

); @@ -187,6 +187,19 @@ export const DocumentCreationButtons: React.FC = ({ Date: Mon, 14 Mar 2022 18:56:35 -0600 Subject: [PATCH 03/10] Define codeowner for @kbn/type-summarizer --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b8563e3e44e8b..1ee6089df8c5f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -219,6 +219,7 @@ /packages/kbn-optimizer/ @elastic/kibana-operations /packages/kbn-pm/ @elastic/kibana-operations /packages/kbn-test/ @elastic/kibana-operations +/packages/kbn-type-summarizer/ @elastic/kibana-operations /packages/kbn-ui-shared-deps-npm/ @elastic/kibana-operations /packages/kbn-ui-shared-deps-src/ @elastic/kibana-operations /packages/kbn-bazel-packages/ @elastic/kibana-operations From 9871111cf687dca5a05e2be59316c2cb2233678c Mon Sep 17 00:00:00 2001 From: Spencer Date: Mon, 14 Mar 2022 20:34:28 -0600 Subject: [PATCH 04/10] [bazel] validate packages/BUILD.bazel in CI (#127241) --- .buildkite/scripts/steps/checks.sh | 1 + .../scripts/steps/checks/bazel_packages.sh | 8 ++ packages/BUILD.bazel | 3 +- packages/kbn-bazel-packages/BUILD.bazel | 3 + .../src/bazel_package.test.ts | 18 +++- .../kbn-bazel-packages/src/bazel_package.ts | 68 ++++++++++++++- ...generate_packages_build_bazel_file.test.ts | 85 ------------------- .../src/generate_packages_build_bazel_file.ts | 49 ----------- packages/kbn-bazel-packages/src/index.ts | 1 - packages/kbn-generate/src/cli.ts | 3 +- .../src/commands/package_command.ts | 21 +++-- .../packages_build_manifest_command.ts | 54 ++++++++++++ .../kbn-generate/src/lib/validate_file.ts | 34 ++++++++ .../templates/package/jest.config.js.ejs | 2 +- .../templates/packages_BUILD.bazel.ejs | 37 ++++++++ src/dev/precommit_hook/casing_check_config.js | 8 +- 16 files changed, 236 insertions(+), 159 deletions(-) create mode 100755 .buildkite/scripts/steps/checks/bazel_packages.sh delete mode 100644 packages/kbn-bazel-packages/src/generate_packages_build_bazel_file.test.ts delete mode 100644 packages/kbn-bazel-packages/src/generate_packages_build_bazel_file.ts create mode 100644 packages/kbn-generate/src/commands/packages_build_manifest_command.ts create mode 100644 packages/kbn-generate/src/lib/validate_file.ts create mode 100644 packages/kbn-generate/templates/packages_BUILD.bazel.ejs diff --git a/.buildkite/scripts/steps/checks.sh b/.buildkite/scripts/steps/checks.sh index 9e335fc3cdea3..bde9e82a4d1e6 100755 --- a/.buildkite/scripts/steps/checks.sh +++ b/.buildkite/scripts/steps/checks.sh @@ -6,6 +6,7 @@ export DISABLE_BOOTSTRAP_VALIDATION=false .buildkite/scripts/bootstrap.sh .buildkite/scripts/steps/checks/commit/commit.sh +.buildkite/scripts/steps/checks/bazel_packages.sh .buildkite/scripts/steps/checks/telemetry.sh .buildkite/scripts/steps/checks/ts_projects.sh .buildkite/scripts/steps/checks/jest_configs.sh diff --git a/.buildkite/scripts/steps/checks/bazel_packages.sh b/.buildkite/scripts/steps/checks/bazel_packages.sh new file mode 100755 index 0000000000000..85268bdcb0f06 --- /dev/null +++ b/.buildkite/scripts/steps/checks/bazel_packages.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/common/util.sh + +echo --- Check Bazel Packages Manifest +node scripts/generate packages_build_manifest --validate diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 66361060f1ee6..f01042cecfae9 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -1,6 +1,7 @@ ################ ################ -## This file is automatically generated, to create a new package use `node scripts/generate package --help` +## This file is automatically generated, to create a new package use `node scripts/generate package --help` or run +## `node scripts/generate packages_build_manifest` to regenerate it from the current state of the repo ################ ################ diff --git a/packages/kbn-bazel-packages/BUILD.bazel b/packages/kbn-bazel-packages/BUILD.bazel index 08ffbe24ba2aa..9adfe80060889 100644 --- a/packages/kbn-bazel-packages/BUILD.bazel +++ b/packages/kbn-bazel-packages/BUILD.bazel @@ -40,6 +40,7 @@ RUNTIME_DEPS = [ "//packages/kbn-utils", "//packages/kbn-std", "@npm//globby", + "@npm//normalize-path", ] # In this array place dependencies necessary to build the types, which will include the @@ -55,7 +56,9 @@ RUNTIME_DEPS = [ TYPES_DEPS = [ "//packages/kbn-utils:npm_module_types", "//packages/kbn-std:npm_module_types", + "@npm//@types/normalize-path", "@npm//globby", + "@npm//normalize-path", ] jsts_transpiler( diff --git a/packages/kbn-bazel-packages/src/bazel_package.test.ts b/packages/kbn-bazel-packages/src/bazel_package.test.ts index 884cf0e646ba0..70d540e43f06a 100644 --- a/packages/kbn-bazel-packages/src/bazel_package.test.ts +++ b/packages/kbn-bazel-packages/src/bazel_package.test.ts @@ -15,24 +15,34 @@ const OWN_BAZEL_BUILD_FILE = Fs.readFileSync(Path.resolve(__dirname, '../BUILD.b describe('hasBuildRule()', () => { it('returns true if there is a rule with the name "build"', () => { - const pkg = new BazelPackage('foo', {}, OWN_BAZEL_BUILD_FILE); + const pkg = new BazelPackage('foo', { name: 'foo' }, OWN_BAZEL_BUILD_FILE); expect(pkg.hasBuildRule()).toBe(true); }); it('returns false if there is no rule with name "build"', () => { - const pkg = new BazelPackage('foo', {}, ``); + const pkg = new BazelPackage('foo', { name: 'foo' }, ``); + expect(pkg.hasBuildRule()).toBe(false); + }); + + it('returns false if there is no BUILD.bazel file', () => { + const pkg = new BazelPackage('foo', { name: 'foo' }); expect(pkg.hasBuildRule()).toBe(false); }); }); describe('hasBuildTypesRule()', () => { it('returns true if there is a rule with the name "build_types"', () => { - const pkg = new BazelPackage('foo', {}, OWN_BAZEL_BUILD_FILE); + const pkg = new BazelPackage('foo', { name: 'foo' }, OWN_BAZEL_BUILD_FILE); expect(pkg.hasBuildTypesRule()).toBe(true); }); it('returns false if there is no rule with name "build_types"', () => { - const pkg = new BazelPackage('foo', {}, ``); + const pkg = new BazelPackage('foo', { name: 'foo' }, ``); + expect(pkg.hasBuildTypesRule()).toBe(false); + }); + + it('returns false if there is no BUILD.bazel file', () => { + const pkg = new BazelPackage('foo', { name: 'foo' }); expect(pkg.hasBuildTypesRule()).toBe(false); }); }); diff --git a/packages/kbn-bazel-packages/src/bazel_package.ts b/packages/kbn-bazel-packages/src/bazel_package.ts index 28170cb68a5d2..35950c9896faf 100644 --- a/packages/kbn-bazel-packages/src/bazel_package.ts +++ b/packages/kbn-bazel-packages/src/bazel_package.ts @@ -6,14 +6,49 @@ * Side Public License, v 1. */ +import { inspect } from 'util'; import Path from 'path'; import Fsp from 'fs/promises'; + +import normalizePath from 'normalize-path'; import { REPO_ROOT } from '@kbn/utils'; const BUILD_RULE_NAME = /(^|\s)name\s*=\s*"build"/; const BUILD_TYPES_RULE_NAME = /(^|\s)name\s*=\s*"build_types"/; +/** + * Simple parsed representation of a package.json file, validated + * by `assertParsedPackageJson()` and extensible as needed in the future + */ +export interface ParsedPackageJson { + /** + * The name of the package, usually `@kbn/`+something + */ + name: string; + /** + * All other fields in the package.json are typed as unknown as all we need at this time is "name" + */ + [key: string]: unknown; +} + +function isObj(v: unknown): v is Record { + return !!(typeof v === 'object' && v); +} + +function assertParsedPackageJson(v: unknown): asserts v is ParsedPackageJson { + if (!isObj(v) || typeof v.name !== 'string') { + throw new Error('Expected parsed package.json to be an object with at least a "name" property'); + } +} + +/** + * Representation of a Bazel Package in the Kibana repository + */ export class BazelPackage { + /** + * Create a BazelPackage object from a package directory. Reads some files from the package and returns + * a Promise for a BazelPackage instance + */ static async fromDir(dir: string) { let pkg; try { @@ -22,6 +57,8 @@ export class BazelPackage { throw new Error(`unable to parse package.json in [${dir}]: ${error.message}`); } + assertParsedPackageJson(pkg); + let buildBazelContent; if (pkg.name !== '@kbn/pm') { try { @@ -31,20 +68,43 @@ export class BazelPackage { } } - return new BazelPackage(Path.relative(REPO_ROOT, dir), pkg, buildBazelContent); + return new BazelPackage(normalizePath(Path.relative(REPO_ROOT, dir)), pkg, buildBazelContent); } constructor( - public readonly repoRelativeDir: string, - public readonly pkg: any, - public readonly buildBazelContent?: string + /** + * Relative path from the root of the repository to the package directory + */ + public readonly normalizedRepoRelativeDir: string, + /** + * Parsed package.json file from the package + */ + public readonly pkg: ParsedPackageJson, + /** + * Content of the BUILD.bazel file + */ + private readonly buildBazelContent?: string ) {} + /** + * Returns true if the package includes a `:build` bazel rule + */ hasBuildRule() { return !!(this.buildBazelContent && BUILD_RULE_NAME.test(this.buildBazelContent)); } + /** + * Returns true if the package includes a `:build_types` bazel rule + */ hasBuildTypesRule() { return !!(this.buildBazelContent && BUILD_TYPES_RULE_NAME.test(this.buildBazelContent)); } + + /** + * Custom inspect handler so that logging variables in scripts/generate doesn't + * print all the BUILD.bazel files + */ + [inspect.custom]() { + return `BazelPackage<${this.normalizedRepoRelativeDir}>`; + } } diff --git a/packages/kbn-bazel-packages/src/generate_packages_build_bazel_file.test.ts b/packages/kbn-bazel-packages/src/generate_packages_build_bazel_file.test.ts deleted file mode 100644 index 2d9fd2ed48adc..0000000000000 --- a/packages/kbn-bazel-packages/src/generate_packages_build_bazel_file.test.ts +++ /dev/null @@ -1,85 +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 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 { generatePackagesBuildBazelFile } from './generate_packages_build_bazel_file'; - -import { BazelPackage } from './bazel_package'; - -it('produces a valid BUILD.bazel file', () => { - const packages = [ - new BazelPackage( - 'foo', - {}, - ` - rule( - name = "build" - ) - rule( - name = "build_types" - ) - ` - ), - new BazelPackage( - 'bar', - {}, - ` - rule( - name= "build_types" - ) - ` - ), - new BazelPackage( - 'bar', - {}, - ` - rule( - name ="build" - ) - ` - ), - new BazelPackage('bar', {}), - ]; - - expect(generatePackagesBuildBazelFile(packages)).toMatchInlineSnapshot(` - "################ - ################ - ## This file is automatically generated, to create a new package use \`node scripts/generate package --help\` - ################ - ################ - - # It will build all declared code packages - filegroup( - name = \\"build_pkg_code\\", - srcs = [ - \\"//foo:build\\", - \\"//bar:build\\", - ], - ) - - # It will build all declared package types - filegroup( - name = \\"build_pkg_types\\", - srcs = [ - \\"//foo:build_types\\", - \\"//bar:build_types\\", - ], - ) - - # Grouping target to call all underlying packages build - # targets so we can build them all at once - # It will auto build all declared code packages and types packages - filegroup( - name = \\"build\\", - srcs = [ - \\":build_pkg_code\\", - \\":build_pkg_types\\" - ], - ) - " - `); -}); diff --git a/packages/kbn-bazel-packages/src/generate_packages_build_bazel_file.ts b/packages/kbn-bazel-packages/src/generate_packages_build_bazel_file.ts deleted file mode 100644 index d1dd3561ed39d..0000000000000 --- a/packages/kbn-bazel-packages/src/generate_packages_build_bazel_file.ts +++ /dev/null @@ -1,49 +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 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 { BazelPackage } from './bazel_package'; - -export function generatePackagesBuildBazelFile(packages: BazelPackage[]) { - return `################ -################ -## This file is automatically generated, to create a new package use \`node scripts/generate package --help\` -################ -################ - -# It will build all declared code packages -filegroup( - name = "build_pkg_code", - srcs = [ -${packages - .flatMap((p) => (p.hasBuildRule() ? ` "//${p.repoRelativeDir}:build",` : [])) - .join('\n')} - ], -) - -# It will build all declared package types -filegroup( - name = "build_pkg_types", - srcs = [ -${packages - .flatMap((p) => (p.hasBuildTypesRule() ? ` "//${p.repoRelativeDir}:build_types",` : [])) - .join('\n')} - ], -) - -# Grouping target to call all underlying packages build -# targets so we can build them all at once -# It will auto build all declared code packages and types packages -filegroup( - name = "build", - srcs = [ - ":build_pkg_code", - ":build_pkg_types" - ], -) -`; -} diff --git a/packages/kbn-bazel-packages/src/index.ts b/packages/kbn-bazel-packages/src/index.ts index 7e73fcd0a63ee..c200882df8ab9 100644 --- a/packages/kbn-bazel-packages/src/index.ts +++ b/packages/kbn-bazel-packages/src/index.ts @@ -8,4 +8,3 @@ export * from './discover_packages'; export type { BazelPackage } from './bazel_package'; -export * from './generate_packages_build_bazel_file'; diff --git a/packages/kbn-generate/src/cli.ts b/packages/kbn-generate/src/cli.ts index 9ade4f68366f5..0b52f5bb4da72 100644 --- a/packages/kbn-generate/src/cli.ts +++ b/packages/kbn-generate/src/cli.ts @@ -12,6 +12,7 @@ import { Render } from './lib/render'; import { ContextExtensions } from './generate_command'; import { PackageCommand } from './commands/package_command'; +import { PackagesBuildManifestCommand } from './commands/packages_build_manifest_command'; /** * Runs the generate CLI. Called by `node scripts/generate` and not intended for use outside of that script @@ -26,6 +27,6 @@ export function runGenerateCli() { }; }, }, - [PackageCommand] + [PackageCommand, PackagesBuildManifestCommand] ).execute(); } diff --git a/packages/kbn-generate/src/commands/package_command.ts b/packages/kbn-generate/src/commands/package_command.ts index 284b3b96a0308..6ce14571e64bb 100644 --- a/packages/kbn-generate/src/commands/package_command.ts +++ b/packages/kbn-generate/src/commands/package_command.ts @@ -13,10 +13,10 @@ import normalizePath from 'normalize-path'; import globby from 'globby'; import { REPO_ROOT } from '@kbn/utils'; -import { discoverBazelPackages, generatePackagesBuildBazelFile } from '@kbn/bazel-packages'; +import { discoverBazelPackages } from '@kbn/bazel-packages'; import { createFailError, createFlagError, isFailError, sortPackageJson } from '@kbn/dev-utils'; -import { ROOT_PKG_DIR, PKG_TEMPLATE_DIR } from '../paths'; +import { TEMPLATE_DIR, ROOT_PKG_DIR, PKG_TEMPLATE_DIR } from '../paths'; import type { GenerateCommand } from '../generate_command'; export const PackageCommand: GenerateCommand = { @@ -49,7 +49,7 @@ export const PackageCommand: GenerateCommand = { const containingDir = flags.dir ? Path.resolve(`${flags.dir}`) : ROOT_PKG_DIR; const packageDir = Path.resolve(containingDir, name.slice(1).replace('/', '-')); - const repoRelativeDir = normalizePath(Path.relative(REPO_ROOT, packageDir)); + const normalizedRepoRelativeDir = normalizePath(Path.relative(REPO_ROOT, packageDir)); try { await Fsp.readdir(packageDir); @@ -107,8 +107,8 @@ export const PackageCommand: GenerateCommand = { name, web, dev, - directoryName: Path.basename(repoRelativeDir), - repoRelativeDir, + directoryName: Path.basename(normalizedRepoRelativeDir), + normalizedRepoRelativeDir, }, }); } @@ -122,17 +122,20 @@ export const PackageCommand: GenerateCommand = { ? [packageJson.devDependencies, packageJson.dependencies] : [packageJson.dependencies, packageJson.devDependencies]; - addDeps[name] = `link:bazel-bin/${repoRelativeDir}`; - addDeps[typePkgName] = `link:bazel-bin/${repoRelativeDir}/npm_module_types`; + addDeps[name] = `link:bazel-bin/${normalizedRepoRelativeDir}`; + addDeps[typePkgName] = `link:bazel-bin/${normalizedRepoRelativeDir}/npm_module_types`; delete removeDeps[name]; delete removeDeps[typePkgName]; await Fsp.writeFile(packageJsonPath, sortPackageJson(JSON.stringify(packageJson))); log.info('Updated package.json file'); - await Fsp.writeFile( + await render.toFile( + Path.resolve(TEMPLATE_DIR, 'packages_BUILD.bazel.ejs'), Path.resolve(REPO_ROOT, 'packages/BUILD.bazel'), - generatePackagesBuildBazelFile(await discoverBazelPackages()) + { + packages: await discoverBazelPackages(), + } ); log.info('Updated packages/BUILD.bazel'); diff --git a/packages/kbn-generate/src/commands/packages_build_manifest_command.ts b/packages/kbn-generate/src/commands/packages_build_manifest_command.ts new file mode 100644 index 0000000000000..9103d56d42a1d --- /dev/null +++ b/packages/kbn-generate/src/commands/packages_build_manifest_command.ts @@ -0,0 +1,54 @@ +/* + * 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 Path from 'path'; +import Fsp from 'fs/promises'; + +import { REPO_ROOT } from '@kbn/utils'; +import { discoverBazelPackages } from '@kbn/bazel-packages'; + +import { TEMPLATE_DIR } from '../paths'; +import { GenerateCommand } from '../generate_command'; +import { validateFile } from '../lib/validate_file'; + +const USAGE = `node scripts/generate packages_build_manifest`; + +export const PackagesBuildManifestCommand: GenerateCommand = { + name: 'packages_build_manifest', + usage: USAGE, + description: 'Generate the packages/BUILD.bazel file', + flags: { + boolean: ['validate'], + help: ` + --validate Rather than writing the generated output to disk, validate that the content on disk is in sync with the + `, + }, + async run({ log, render, flags }) { + const validate = !!flags.validate; + + const packages = await discoverBazelPackages(); + const dest = Path.resolve(REPO_ROOT, 'packages/BUILD.bazel'); + const relDest = Path.relative(process.cwd(), dest); + + const content = await render.toString( + Path.join(TEMPLATE_DIR, 'packages_BUILD.bazel.ejs'), + dest, + { + packages, + } + ); + + if (validate) { + await validateFile(log, USAGE, dest, content); + return; + } + + await Fsp.writeFile(dest, content); + log.success('Wrote', relDest); + }, +}; diff --git a/packages/kbn-generate/src/lib/validate_file.ts b/packages/kbn-generate/src/lib/validate_file.ts new file mode 100644 index 0000000000000..d4f3640a45471 --- /dev/null +++ b/packages/kbn-generate/src/lib/validate_file.ts @@ -0,0 +1,34 @@ +/* + * 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 Fsp from 'fs/promises'; +import Path from 'path'; + +import { ToolingLog, createFailError, diffStrings } from '@kbn/dev-utils'; + +export async function validateFile(log: ToolingLog, usage: string, path: string, expected: string) { + const relPath = Path.relative(process.cwd(), path); + + let current; + try { + current = await Fsp.readFile(path, 'utf8'); + } catch (error) { + if (error && error.code === 'ENOENT') { + throw createFailError(`${relPath} is missing, please run "${usage}" and commit the result`); + } + + throw error; + } + + if (current !== expected) { + log.error(`${relPath} is outdated:\n${diffStrings(expected, current)}`); + throw createFailError(`${relPath} is outdated, please run "${usage}" and commit the result`); + } + + log.success(`${relPath} is valid`); +} diff --git a/packages/kbn-generate/templates/package/jest.config.js.ejs b/packages/kbn-generate/templates/package/jest.config.js.ejs index 1846d6a8f96f5..6a65cc6b304c5 100644 --- a/packages/kbn-generate/templates/package/jest.config.js.ejs +++ b/packages/kbn-generate/templates/package/jest.config.js.ejs @@ -1,5 +1,5 @@ module.exports = { preset: <%- js(pkg.web ? '@kbn/test' : '@kbn/test/jest_node') %>, rootDir: '../..', - roots: [<%- js(`/${pkg.repoRelativeDir}`) %>], + roots: [<%- js(`/${pkg.normalizedRepoRelativeDir}`) %>], }; diff --git a/packages/kbn-generate/templates/packages_BUILD.bazel.ejs b/packages/kbn-generate/templates/packages_BUILD.bazel.ejs new file mode 100644 index 0000000000000..43dd306d3cbb7 --- /dev/null +++ b/packages/kbn-generate/templates/packages_BUILD.bazel.ejs @@ -0,0 +1,37 @@ +################ +################ +## This file is automatically generated, to create a new package use `node scripts/generate package --help` or run +## `node scripts/generate packages_build_manifest` to regenerate it from the current state of the repo +################ +################ + +# It will build all declared code packages +filegroup( + name = "build_pkg_code", + srcs = [ +<% for (const p of packages.filter(p => p.hasBuildRule())) { _%> + "//<%- p.normalizedRepoRelativeDir %>:build", +<% } _%> + ], +) + +# It will build all declared package types +filegroup( + name = "build_pkg_types", + srcs = [ +<% for (const p of packages.filter(p => p.hasBuildTypesRule())) { _%> + "//<%- p.normalizedRepoRelativeDir %>:build_types", +<% } _%> + ], +) + +# Grouping target to call all underlying packages build +# targets so we can build them all at once +# It will auto build all declared code packages and types packages +filegroup( + name = "build", + srcs = [ + ":build_pkg_code", + ":build_pkg_types" + ], +) diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index 86448be7c3e53..eb65e0b752174 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -67,6 +67,9 @@ export const IGNORE_FILE_GLOBS = [ // Buildkite '.buildkite/**/*', + + // generator templates use weird filenames based on the requirements for the files they're generating + 'packages/kbn-generate/templates/**/*', ]; /** @@ -107,10 +110,7 @@ export const IGNORE_DIRECTORY_GLOBS = [ * * @type {Array} */ -export const REMOVE_EXTENSION = [ - 'packages/kbn-plugin-generator/template/**/*.ejs', - 'packages/kbn-generate/templates/**/*.ejs', -]; +export const REMOVE_EXTENSION = ['packages/kbn-plugin-generator/template/**/*.ejs']; /** * DO NOT ADD FILES TO THIS LIST!! From c060b9f6f76c4971da67c9101e7466db6b727df6 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Mon, 14 Mar 2022 21:48:29 -0600 Subject: [PATCH 05/10] [Security Solutions] Increases timeout and unskips detection tests (#126294) ## Summary Increases the timeouts from 2 minutes to now 5 minutes and unskips the detection tests. If any of the tests fail consistently then I will skip just those individual tests instead of the whole suit. See https://github.com/elastic/kibana/issues/125851 and https://github.com/elastic/elasticsearch/issues/84256 This could cause issues with: https://github.com/elastic/kibana/issues/125319 If so, then we will have to deal with the cloud based tests in a different way but in reality we need the extra time as some test cases do take a while to run on CI. This also: * Removes skips around code that have been in there for a while and adds documentation to the parts that are left over. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../basic/tests/open_close_signals.ts | 2 +- .../basic/tests/query_signals.ts | 2 ++ .../security_and_spaces/tests/create_exceptions.ts | 3 +-- .../security_and_spaces/tests/create_index.ts | 1 + .../security_and_spaces/tests/create_ml.ts | 3 +-- .../security_and_spaces/tests/create_rules.ts | 6 ++---- .../security_and_spaces/tests/create_threat_matching.ts | 3 +-- .../tests/finalize_signals_migrations.ts | 1 + .../security_and_spaces/tests/generating_signals.ts | 5 ++--- .../security_and_spaces/tests/index.ts | 3 +-- .../security_and_spaces/tests/open_close_signals.ts | 8 +++++--- x-pack/test/detection_engine_api_integration/utils.ts | 3 ++- 12 files changed, 20 insertions(+), 20 deletions(-) diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts b/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts index fc0072de05f5e..f58dee11c48da 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts @@ -34,7 +34,7 @@ export default ({ getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const log = getService('log'); - describe.skip('open_close_signals', () => { + describe('open_close_signals', () => { describe('tests with auditbeat data', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/query_signals.ts b/x-pack/test/detection_engine_api_integration/basic/tests/query_signals.ts index 3ef0a12cf70dc..48d13e8b2afd6 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/query_signals.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/query_signals.ts @@ -39,6 +39,7 @@ export default ({ getService }: FtrProviderContext) => { }); }); + // This fails and should be investigated or removed if it no longer applies it.skip('should not give errors when querying and the signals index does exist and is empty', async () => { await createSignalsIndex(supertest, log); const { body } = await supertest @@ -160,6 +161,7 @@ export default ({ getService }: FtrProviderContext) => { }); }); + // This fails and should be investigated or removed if it no longer applies it.skip('should not give errors when querying and the signals index does exist and is empty', async () => { await createSignalsIndex(supertest, log); const { body } = await supertest diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts index 818ba3b366e40..0dfc753be402c 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts @@ -63,8 +63,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const es = getService('es'); - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/125851 - describe.skip('create_rules_with_exceptions', () => { + describe('create_rules_with_exceptions', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_index.ts index 17d6b3957637a..6d867877e0dc3 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_index.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_index.ts @@ -38,6 +38,7 @@ export default ({ getService }: FtrProviderContext) => { await esArchiver.unload('x-pack/test/functional/es_archives/signals/index_alias_clash'); }); + // This fails and should be investigated or removed if it no longer applies it.skip('should report that signals index does not exist', async () => { const { body } = await supertest.get(DETECTION_ENGINE_INDEX_URL).send().expect(404); expect(body).to.eql({ message: 'index for this space does not exist', status_code: 404 }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts index e2ce3922f2d3f..343db03c2ae27 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts @@ -92,8 +92,7 @@ export default ({ getService }: FtrProviderContext) => { return body; } - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/125851 - describe.skip('Generating signals from ml anomalies', () => { + describe('Generating signals from ml anomalies', () => { before(async () => { // Order is critical here: auditbeat data must be loaded before attempting to start the ML job, // as the job looks for certain indices on start diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts index 1075a63742777..82899ff0de8f7 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts @@ -44,8 +44,7 @@ export default ({ getService }: FtrProviderContext) => { const supertestWithoutAuth = getService('supertestWithoutAuth'); const log = getService('log'); - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/125851 - describe.skip('create_rules', () => { + describe('create_rules', () => { describe('creating rules', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); @@ -106,8 +105,7 @@ export default ({ getService }: FtrProviderContext) => { await waitForRuleSuccessOrStatus(supertest, log, body.id); }); - // TODO: does the below test work? - it.skip('should create a single rule with a rule_id and an index pattern that does not match anything available and partial failure for the rule', async () => { + it('should create a single rule with a rule_id and an index pattern that does not match anything available and partial failure for the rule', async () => { const simpleRule = getRuleForSignalTesting(['does-not-exist-*']); const { body } = await supertest .post(DETECTION_ENGINE_RULES_URL) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts index b5b232f70ec89..346998e7af261 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts @@ -69,8 +69,7 @@ export default ({ getService }: FtrProviderContext) => { /** * Specific api integration tests for threat matching rule type */ - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/125851 - describe.skip('create_threat_matching', () => { + describe('create_threat_matching', () => { describe('creating threat match rule', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/finalize_signals_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/finalize_signals_migrations.ts index df06647c2a8b5..8733c645112d1 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/finalize_signals_migrations.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/finalize_signals_migrations.ts @@ -183,6 +183,7 @@ export default ({ getService }: FtrProviderContext): void => { expect(statusAfter.map((s) => s.is_outdated)).to.eql([false, false]); }); + // This fails and should be investigated or removed if it no longer applies it.skip('deletes the underlying migration task', async () => { await waitFor( async () => { diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts index 761792e29ea1d..11c5af219de4c 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts @@ -71,8 +71,7 @@ export default ({ getService }: FtrProviderContext) => { const es = getService('es'); const log = getService('log'); - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/125851 - describe.skip('Generating signals from source indexes', () => { + describe('Generating signals from source indexes', () => { beforeEach(async () => { await deleteSignalsIndex(supertest, log); await createSignalsIndex(supertest, log); @@ -1160,7 +1159,7 @@ export default ({ getService }: FtrProviderContext) => { }); }); - describe.skip('Signal deduplication', async () => { + describe('Signal deduplication', async () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts index d234c70de5240..a9bda19638041 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts @@ -9,8 +9,7 @@ import { FtrProviderContext } from '../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default ({ loadTestFile }: FtrProviderContext): void => { - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/125851 - describe.skip('detection engine api security and spaces enabled', function () { + describe('detection engine api security and spaces enabled', function () { describe('', function () { this.tags('ciGroup11'); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts index f9a5a3283bd2f..6138995bdc02f 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts @@ -39,7 +39,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); describe('open_close_signals', () => { - describe.skip('validation checks', () => { + describe('validation checks', () => { it('should not give errors when querying and the signals index does not exist yet', async () => { const { body } = await supertest .post(DETECTION_ENGINE_SIGNALS_STATUS_URL) @@ -165,7 +165,8 @@ export default ({ getService }: FtrProviderContext) => { expect(everySignalClosed).to.eql(true); }); - it('should be able to close signals with t1 analyst user', async () => { + // This fails and should be investigated or removed if it no longer applies + it.skip('should be able to close signals with t1 analyst user', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, log, rule); await waitForRuleSuccessOrStatus(supertest, log, id); @@ -200,7 +201,8 @@ export default ({ getService }: FtrProviderContext) => { await deleteUserAndRole(getService, ROLES.t1_analyst); }); - it('should be able to close signals with soc_manager user', async () => { + // This fails and should be investigated or removed if it no longer applies + it.skip('should be able to close signals with soc_manager user', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, log, rule); await waitForRuleSuccessOrStatus(supertest, log, id); diff --git a/x-pack/test/detection_engine_api_integration/utils.ts b/x-pack/test/detection_engine_api_integration/utils.ts index 496faa3c5e421..545bd20fc777b 100644 --- a/x-pack/test/detection_engine_api_integration/utils.ts +++ b/x-pack/test/detection_engine_api_integration/utils.ts @@ -457,6 +457,7 @@ export const getSimpleRulePreviewOutput = ( ) => ({ logs, previewId, + isAborted: false, }); export const resolveSimpleRuleOutput = ( @@ -924,7 +925,7 @@ export const waitFor = async ( functionToTest: () => Promise, functionName: string, log: ToolingLog, - maxTimeout: number = 100000, + maxTimeout: number = 400000, timeoutWait: number = 250 ): Promise => { let found = false; From 553374816224ac901327c9dae5e75b8ffa70b79e Mon Sep 17 00:00:00 2001 From: Dmitry Tomashevich <39378793+Dmitriynj@users.noreply.github.com> Date: Tue, 15 Mar 2022 12:10:18 +0500 Subject: [PATCH 06/10] [Discover] Fix "encoded URL params in context page" cloud test permissions (#126470) * [Discover] fix cloud test for encoded param in context * [Discover] improve test description wording * [Discover] apply suggestion Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../apps/discover/_context_encoded_url_param.ts | 12 +++++++----- test/functional/config.js | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/test/functional/apps/discover/_context_encoded_url_param.ts b/test/functional/apps/discover/_context_encoded_url_param.ts index 83ac63afd915f..fdbee7a637f46 100644 --- a/test/functional/apps/discover/_context_encoded_url_param.ts +++ b/test/functional/apps/discover/_context_encoded_url_param.ts @@ -12,30 +12,32 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const dataGrid = getService('dataGrid'); const kibanaServer = getService('kibanaServer'); + const security = getService('security'); const PageObjects = getPageObjects(['common', 'discover', 'timePicker', 'settings', 'header']); const testSubjects = getService('testSubjects'); const es = getService('es'); - describe('context encoded id param', () => { + describe('encoded URL params in context page', () => { before(async function () { + await security.testUser.setRoles(['kibana_admin', 'context_encoded_param']); await PageObjects.common.navigateToApp('settings'); await es.transport.request({ - path: '/includes-plus-symbol-doc-id/_doc/1+1=2', + path: '/context-encoded-param/_doc/1+1=2', method: 'PUT', body: { username: 'Dmitry', '@timestamp': '2015-09-21T09:30:23', }, }); - await PageObjects.settings.createIndexPattern('includes-plus-symbol-doc-id'); + await PageObjects.settings.createIndexPattern('context-encoded-param'); await kibanaServer.uiSettings.update({ 'doc_table:legacy': false }); await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); await PageObjects.common.navigateToApp('discover'); }); - it('should navigate to context page correctly', async () => { - await PageObjects.discover.selectIndexPattern('includes-plus-symbol-doc-id'); + it('should navigate correctly', async () => { + await PageObjects.discover.selectIndexPattern('context-encoded-param'); await PageObjects.header.waitUntilLoadingHasFinished(); // navigate to the context view diff --git a/test/functional/config.js b/test/functional/config.js index 389f432641acf..bfd2da7518fb7 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -216,6 +216,21 @@ export default async function ({ readConfigFile }) { kibana: [], }, + context_encoded_param: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['context-encoded-param'], + privileges: ['read', 'view_index_metadata', 'manage', 'create_index', 'index'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + kibana_sample_read: { elasticsearch: { cluster: [], From f4bd49b5927de5f15882f63064338e92b722abad Mon Sep 17 00:00:00 2001 From: Esteban Beltran Date: Tue, 15 Mar 2022 08:55:38 +0100 Subject: [PATCH 07/10] [Cases] Only enable save if changes were made to the connector form (#127455) --- .../components/edit_connector/index.test.tsx | 115 ++++++++++++++++-- .../components/edit_connector/index.tsx | 24 +++- 2 files changed, 123 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/cases/public/components/edit_connector/index.test.tsx b/x-pack/plugins/cases/public/components/edit_connector/index.test.tsx index bcc700c543f7e..0512501bf5e11 100644 --- a/x-pack/plugins/cases/public/components/edit_connector/index.test.tsx +++ b/x-pack/plugins/cases/public/components/edit_connector/index.test.tsx @@ -14,6 +14,8 @@ import { TestProviders } from '../../common/mock'; import { connectorsMock } from '../../containers/configure/mock'; import { basicCase, basicPush, caseUserActions } from '../../containers/mock'; import { useKibana } from '../../common/lib/kibana'; +import { CaseConnector } from '../../containers/configure/types'; +import userEvent from '@testing-library/user-event'; jest.mock('../../common/lib/kibana'); const useKibanaMock = useKibana as jest.Mocked; @@ -29,18 +31,20 @@ const caseServices = { hasDataToPush: true, }, }; -const defaultProps: EditConnectorProps = { - caseData: basicCase, - caseServices, - connectorName: connectorsMock[0].name, - connectors: connectorsMock, - hasDataToPush: true, - isLoading: false, - isValidConnector: true, - onSubmit, - updateCase, - userActions: caseUserActions, - userCanCrud: true, +const getDefaultProps = (): EditConnectorProps => { + return { + caseData: basicCase, + caseServices, + connectorName: connectorsMock[0].name, + connectors: connectorsMock, + hasDataToPush: true, + isLoading: false, + isValidConnector: true, + onSubmit, + updateCase, + userActions: caseUserActions, + userCanCrud: true, + }; }; describe('EditConnector ', () => { @@ -53,6 +57,7 @@ describe('EditConnector ', () => { }); it('Renders servicenow connector from case initially', async () => { + const defaultProps = getDefaultProps(); const serviceNowProps = { ...defaultProps, caseData: { @@ -71,6 +76,7 @@ describe('EditConnector ', () => { }); it('Renders no connector, and then edit', async () => { + const defaultProps = getDefaultProps(); const wrapper = mount( @@ -92,6 +98,7 @@ describe('EditConnector ', () => { }); it('Edit external service on submit', async () => { + const defaultProps = getDefaultProps(); const wrapper = mount( @@ -111,6 +118,7 @@ describe('EditConnector ', () => { }); it('Revert to initial external service on error', async () => { + const defaultProps = getDefaultProps(); onSubmit.mockImplementation((connector, onSuccess, onError) => { onError(new Error('An error has occurred')); }); @@ -155,11 +163,15 @@ describe('EditConnector ', () => { }); it('Resets selector on cancel', async () => { + const defaultProps = getDefaultProps(); const props = { ...defaultProps, caseData: { ...defaultProps.caseData, - connector: { ...defaultProps.caseData.connector, id: 'servicenow-1' }, + connector: { + ...defaultProps.caseData.connector, + id: 'servicenow-1', + }, }, }; @@ -185,6 +197,7 @@ describe('EditConnector ', () => { }); it('Renders loading spinner', async () => { + const defaultProps = getDefaultProps(); const props = { ...defaultProps, isLoading: true }; const wrapper = mount( @@ -197,6 +210,7 @@ describe('EditConnector ', () => { }); it('does not allow the connector to be edited when the user does not have write permissions', async () => { + const defaultProps = getDefaultProps(); const props = { ...defaultProps, userCanCrud: false }; const wrapper = mount( @@ -211,6 +225,7 @@ describe('EditConnector ', () => { }); it('displays the permissions error message when one is provided', async () => { + const defaultProps = getDefaultProps(); const props = { ...defaultProps, permissionsError: 'error message' }; const wrapper = mount( @@ -232,6 +247,7 @@ describe('EditConnector ', () => { }); it('displays the callout message when none is selected', async () => { + const defaultProps = getDefaultProps(); const props = { ...defaultProps, connectors: [] }; const wrapper = mount( @@ -247,4 +263,77 @@ describe('EditConnector ', () => { expect(wrapper.find(`[data-test-subj="push-callouts"]`).exists()).toEqual(true); }); }); + + it('disables the save button until changes are done ', async () => { + const defaultProps = getDefaultProps(); + const serviceNowProps = { + ...defaultProps, + caseData: { + ...defaultProps.caseData, + connector: { + ...defaultProps.caseData.connector, + id: 'servicenow-1', + fields: { + urgency: null, + severity: null, + impact: null, + category: null, + subcategory: null, + }, + } as CaseConnector, + }, + }; + const result = render( + + + + ); + + // the save button should be disabled + userEvent.click(result.getByTestId('connector-edit-button')); + expect(result.getByTestId('edit-connectors-submit')).toBeDisabled(); + + // simulate changing the connector + userEvent.click(result.getByTestId('dropdown-connectors')); + userEvent.click(result.getAllByTestId('dropdown-connector-no-connector')[0]); + expect(result.getByTestId('edit-connectors-submit')).toBeEnabled(); + + // this strange assertion is required because of existing race conditions inside the EditConnector component + await waitFor(() => { + expect(true).toBeTruthy(); + }); + }); + + it('disables the save button when no connector is the default', async () => { + const defaultProps = getDefaultProps(); + const noneConnector = { + ...defaultProps, + caseData: { + ...defaultProps.caseData, + connector: { + id: 'none', + fields: null, + } as CaseConnector, + }, + }; + const result = render( + + + + ); + + // the save button should be disabled + userEvent.click(result.getByTestId('connector-edit-button')); + expect(result.getByTestId('edit-connectors-submit')).toBeDisabled(); + + // simulate changing the connector + userEvent.click(result.getByTestId('dropdown-connectors')); + userEvent.click(result.getAllByTestId('dropdown-connector-resilient-2')[0]); + expect(result.getByTestId('edit-connectors-submit')).toBeEnabled(); + + // this strange assertion is required because of existing race conditions inside the EditConnector component + await waitFor(() => { + expect(true).toBeTruthy(); + }); + }); }); diff --git a/x-pack/plugins/cases/public/components/edit_connector/index.tsx b/x-pack/plugins/cases/public/components/edit_connector/index.tsx index 5ab27e3e32009..7db20170c7857 100644 --- a/x-pack/plugins/cases/public/components/edit_connector/index.tsx +++ b/x-pack/plugins/cases/public/components/edit_connector/index.tsx @@ -4,8 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import React, { useCallback, useEffect, useReducer } from 'react'; +import React, { useCallback, useEffect, useReducer, useState } from 'react'; import deepEqual from 'fast-deep-equal'; import { EuiText, @@ -22,7 +21,7 @@ import { isEmpty, noop } from 'lodash/fp'; import { FieldConfig, Form, UseField, useForm } from '../../common/shared_imports'; import { Case } from '../../../common/ui/types'; -import { ActionConnector, ConnectorTypeFields } from '../../../common/api'; +import { ActionConnector, ConnectorTypeFields, NONE_CONNECTOR_ID } from '../../../common/api'; import { ConnectorSelector } from '../connector_selector/form'; import { ConnectorFieldsForm } from '../connectors/fields_form'; import { CaseUserActions } from '../../containers/types'; @@ -133,6 +132,9 @@ export const EditConnector = React.memo( schema, }); + // by default save if disabled + const [enableSave, setEnableSave] = useState(false); + const { setFieldValue, submit } = form; const [{ currentConnector, fields, editConnector }, dispatch] = useReducer( @@ -140,6 +142,21 @@ export const EditConnector = React.memo( { ...initialState, fields: caseFields } ); + // only enable the save button if changes were made to the previous selected + // connector or its fields + useEffect(() => { + // null and none are equivalent to `no connector`. + // This makes sure we don't enable the button when the "no connector" option is selected + // by default. e.g. when a case is created without a selector + const isNoConnectorDeafultValue = + currentConnector === null && selectedConnector === NONE_CONNECTOR_ID; + const enable = + (!isNoConnectorDeafultValue && currentConnector?.id !== selectedConnector) || + !deepEqual(fields, caseFields); + + setEnableSave(enable); + }, [caseFields, currentConnector, fields, selectedConnector]); + useEffect(() => { // Initialize the current connector with the connector information attached to the case if we can find that // connector in the retrieved connectors from the API call @@ -330,6 +347,7 @@ export const EditConnector = React.memo( Date: Tue, 15 Mar 2022 09:48:36 +0100 Subject: [PATCH 08/10] [expressions] removing export * (#127450) --- .../expression_partition_vis/public/types.ts | 5 +- .../common/expression_types/specs/boolean.ts | 8 +- .../common/expression_types/specs/num.ts | 10 +- .../common/expression_types/specs/number.ts | 6 +- .../common/expression_types/specs/string.ts | 6 +- src/plugins/expressions/common/index.ts | 223 +++++++++++++++++- src/plugins/expressions/public/index.ts | 5 +- src/plugins/expressions/server/index.ts | 4 - 8 files changed, 230 insertions(+), 37 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/types.ts b/src/plugins/chart_expressions/expression_partition_vis/public/types.ts index aa87124ed2b4b..50fb15cc053a3 100755 --- a/src/plugins/chart_expressions/expression_partition_vis/public/types.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/types.ts @@ -7,7 +7,10 @@ */ import type { ValueClickContext } from '../../../embeddable/public'; import { ChartsPluginSetup } from '../../../charts/public'; -import { ExpressionsPublicPlugin, ExpressionsServiceStart } from '../../../expressions/public'; +import { + Plugin as ExpressionsPublicPlugin, + ExpressionsServiceStart, +} from '../../../expressions/public'; export type ExpressionPartitionVisPluginSetup = void; export type ExpressionPartitionVisPluginStart = void; diff --git a/src/plugins/expressions/common/expression_types/specs/boolean.ts b/src/plugins/expressions/common/expression_types/specs/boolean.ts index 3d8fe43ced3f4..a06b2c69c47b6 100644 --- a/src/plugins/expressions/common/expression_types/specs/boolean.ts +++ b/src/plugins/expressions/common/expression_types/specs/boolean.ts @@ -16,11 +16,11 @@ export const boolean: ExpressionTypeDefinition<'boolean', boolean> = { name, from: { null: () => false, - number: (n) => Boolean(n), - string: (s) => Boolean(s), + number: (n: number) => Boolean(n), + string: (s: string) => Boolean(s), }, to: { - render: (value): ExpressionValueRender<{ text: string }> => { + render: (value: boolean): ExpressionValueRender<{ text: string }> => { const text = `${value}`; return { type: 'render', @@ -28,7 +28,7 @@ export const boolean: ExpressionTypeDefinition<'boolean', boolean> = { value: { text }, }; }, - datatable: (value): Datatable => ({ + datatable: (value: boolean): Datatable => ({ type: 'datatable', columns: [{ id: 'value', name: 'value', meta: { type: name } }], rows: [{ value }], diff --git a/src/plugins/expressions/common/expression_types/specs/num.ts b/src/plugins/expressions/common/expression_types/specs/num.ts index 14e65a7ca39af..52651f8939ccc 100644 --- a/src/plugins/expressions/common/expression_types/specs/num.ts +++ b/src/plugins/expressions/common/expression_types/specs/num.ts @@ -25,11 +25,11 @@ export const num: ExpressionTypeDefinition<'num', ExpressionValueNum> = { type: 'num', value: 0, }), - boolean: (b) => ({ + boolean: (b: boolean) => ({ type: 'num', value: Number(b), }), - string: (n) => { + string: (n: string) => { const value = Number(n); if (Number.isNaN(value)) { throw new Error( @@ -46,13 +46,13 @@ export const num: ExpressionTypeDefinition<'num', ExpressionValueNum> = { value, }; }, - '*': (value) => ({ + '*': (value: unknown) => ({ type: 'num', value: Number(value), }), }, to: { - render: ({ value }): ExpressionValueRender<{ text: string }> => { + render: ({ value }: { value: number }): ExpressionValueRender<{ text: string }> => { const text = `${value}`; return { type: 'render', @@ -60,7 +60,7 @@ export const num: ExpressionTypeDefinition<'num', ExpressionValueNum> = { value: { text }, }; }, - datatable: ({ value }): Datatable => ({ + datatable: ({ value }: { value: number }): Datatable => ({ type: 'datatable', columns: [{ id: 'value', name: 'value', meta: { type: 'number' } }], rows: [{ value }], diff --git a/src/plugins/expressions/common/expression_types/specs/number.ts b/src/plugins/expressions/common/expression_types/specs/number.ts index bc2a70cceed68..c1a083d6ebe7a 100644 --- a/src/plugins/expressions/common/expression_types/specs/number.ts +++ b/src/plugins/expressions/common/expression_types/specs/number.ts @@ -17,8 +17,8 @@ export const number: ExpressionTypeDefinition = { name, from: { null: () => 0, - boolean: (b) => Number(b), - string: (n) => { + boolean: (b: boolean) => Number(b), + string: (n: number) => { const value = Number(n); if (Number.isNaN(value)) { throw new Error( @@ -42,7 +42,7 @@ export const number: ExpressionTypeDefinition = { value: { text }, }; }, - datatable: (value): Datatable => ({ + datatable: (value: number): Datatable => ({ type: 'datatable', columns: [{ id: 'value', name: 'value', meta: { type: 'number' } }], rows: [{ value }], diff --git a/src/plugins/expressions/common/expression_types/specs/string.ts b/src/plugins/expressions/common/expression_types/specs/string.ts index b5bee419ade5a..5b8b272c903d9 100644 --- a/src/plugins/expressions/common/expression_types/specs/string.ts +++ b/src/plugins/expressions/common/expression_types/specs/string.ts @@ -16,8 +16,8 @@ export const string: ExpressionTypeDefinition = { name, from: { null: () => '', - boolean: (b) => String(b), - number: (n) => String(n), + boolean: (b: boolean) => String(b), + number: (n: number) => String(n), }, to: { render: (text: T): ExpressionValueRender<{ text: T }> => { @@ -27,7 +27,7 @@ export const string: ExpressionTypeDefinition = { value: { text }, }; }, - datatable: (value): Datatable => ({ + datatable: (value: string): Datatable => ({ type: 'datatable', columns: [{ id: 'value', name: 'value', meta: { type: 'string' } }], rows: [{ value }], diff --git a/src/plugins/expressions/common/index.ts b/src/plugins/expressions/common/index.ts index cc5f2785a2b8e..dcacf49265430 100644 --- a/src/plugins/expressions/common/index.ts +++ b/src/plugins/expressions/common/index.ts @@ -6,16 +6,213 @@ * Side Public License, v 1. */ -// TODO: https://github.com/elastic/kibana/issues/109902 -/* eslint-disable @kbn/eslint/no_export_all */ - -export * from './types'; -export * from './ast'; -export * from './fonts'; -export * from './expression_types'; -export * from './expression_functions'; -export * from './expression_renderers'; -export * from './executor'; -export * from './execution'; -export * from './service'; -export * from './util'; +export { + BackgroundRepeat, + BackgroundSize, + Overflow, + FontStyle, + FontWeight, + FontSizeUnit, + TextDecoration, + TextAlignment, +} from './types'; +export type { + ExpressionTypeStyle, + CSSStyle, + Style, + TypeString, + TypeToString, + UnmappedTypeStrings, + KnownTypeToString, + IRegistry, + ContainerStyle, +} from './types'; +export { + buildExpression, + buildExpressionFunction, + formatExpression, + isExpressionAstBuilder, + parseExpression, + parse, + isExpressionAst, + format, +} from './ast'; +export type { + ExpressionAstExpression, + ExpressionAstExpressionBuilder, + ExpressionAstFunction, + ExpressionAstFunctionBuilder, + ExpressionAstNode, + ExpressionAstArgument, + ExpressionAstFunctionDebug, + InferFunctionDefinition, +} from './ast'; +export { + americanTypewriter, + inter, + arial, + fonts, + bookAntiqua, + brushScript, + didot, + futura, + chalkboard, + gillSans, + openSans, + helveticaNeue, + hoeflerText, + lucidaGrande, + myriad, + optima, + palatino, + baskerville, +} from './fonts'; +export type { Font, FontLabel, FontValue } from './fonts'; +export { + num, + filter, + error, + image, + render, + number, + datatable, + nullType, + range, + pointseries, + serializeProvider, + typeSpecs, + string, + shape, + style, + uiSetting, + boolean, + isExpressionValueError, + getType, + unboxExpressionValue, + isDatatable, + ExpressionType, +} from './expression_types'; +export type { + AnyExpressionTypeDefinition, + ExpressionValueError, + ExpressionValueNum, + ExpressionValueRender, + ExpressionValueUnboxed, + ExpressionValueFilter, + ExpressionTypeDefinition, + ExpressionValueConverter, + ExpressionValueBoxed, + ExpressionValue, + Render, + ExpressionImage, + DatatableColumnType, + DatatableRow, + Datatable, + DatatableColumn, + DatatableColumnMeta, + InterpreterErrorType, + SerializedDatatable, + PointSeries, + PointSeriesColumn, + PointSeriesColumnName, + PointSeriesColumns, + PointSeriesRow, + Range, + UiSetting, +} from './expression_types'; +export { + derivative, + movingAverage, + mapColumn, + math, + mathColumn, + clog, + createTable, + font, + variableSet, + variable, + theme, + cumulativeSum, + overallMetric, + getUiSettingFn, + buildResultColumns, + getBucketIdentifier, + ExpressionFunction, +} from './expression_functions'; +export type { + AnyExpressionFunctionDefinition, + ExpressionFunctionDefinition, + ExpressionFunctionDefinitions, + ExpressionFunctionParameter, + ExpressionFunctionDerivative, + ExpressionFunctionClog, + CreateTableArguments, + CumulativeSumArgs, + ExpressionFunctionFont, + ExpressionFunctionTheme, + ExpressionFunctionUiSetting, + ExpressionFunctionVar, + ExpressionFunctionVarSet, + DerivativeArgs, + ExpressionFunctionCumulativeSum, + ExpressionFunctionMovingAverage, + ExpressionFunctionOverallMetric, + FontArguments, + MapColumnArguments, + MathColumnArguments, + MathArguments, + MovingAverageArgs, + OverallMetricArgs, + ArgumentType, + MathInput, + UiSettingArguments, +} from './expression_functions'; +export { ExpressionRenderer } from './expression_renderers'; +export type { + AnyExpressionRenderDefinition, + ExpressionRendererRegistry, + ExpressionRenderDefinition, + IInterpreterRenderEvent, + IInterpreterRenderHandlers, + IInterpreterRenderUpdateParams, + RenderMode, +} from './expression_renderers'; +export { createExecutorContainer, pureSelectors, pureTransitions, defaultState } from './executor'; +export type { + Executor, + ExecutorContainer, + ExecutorState, + ExecutorPureSelectors, + ExecutorPureTransitions, + ExpressionExecOptions, + FunctionsRegistry, + TypesRegistry, +} from './executor'; +export { createExecutionContainer, executionPureTransitions } from './execution'; +export type { + Execution, + ExecutionContract, + ExecutionContainer, + ExecutionContext, + ExecutionParams, + ExecutionState, + ExecutionPureTransitions, + ExecutionResult, + DefaultInspectorAdapters, +} from './execution'; +export { ExpressionsService } from './service'; +export type { + ExpressionsServiceSetup, + ExpressionsServiceStart, + ExpressionExecutionParams, + ExpressionServiceParams, +} from './service'; +export { + createDefaultInspectorAdapters, + createMockContext, + createError, + getByAlias, + ExpressionsInspectorAdapter, + TablesAdapter, +} from './util'; +export type { SerializedError, ErrorLike } from './util'; diff --git a/src/plugins/expressions/public/index.ts b/src/plugins/expressions/public/index.ts index 24116025267ba..3baf85cec22de 100644 --- a/src/plugins/expressions/public/index.ts +++ b/src/plugins/expressions/public/index.ts @@ -6,9 +6,6 @@ * Side Public License, v 1. */ -// TODO: https://github.com/elastic/kibana/issues/109902 -/* eslint-disable @kbn/eslint/no_export_all */ - import './index.scss'; import { PluginInitializerContext } from '../../../core/public'; @@ -16,7 +13,7 @@ import { ExpressionsPublicPlugin } from './plugin'; // Kibana Platform. export { ExpressionsPublicPlugin as Plugin }; -export * from './plugin'; +export type { ExpressionsSetup, ExpressionsStart } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { return new ExpressionsPublicPlugin(initializerContext); } diff --git a/src/plugins/expressions/server/index.ts b/src/plugins/expressions/server/index.ts index db94ed6dbd440..4baac3dd5285b 100644 --- a/src/plugins/expressions/server/index.ts +++ b/src/plugins/expressions/server/index.ts @@ -6,9 +6,6 @@ * Side Public License, v 1. */ -// TODO: https://github.com/elastic/kibana/issues/109902 -/* eslint-disable @kbn/eslint/no_export_all */ - import { PluginInitializerContext } from 'src/core/server'; import { ExpressionsServerPlugin } from './plugin'; @@ -16,7 +13,6 @@ export type { ExpressionsServerSetup, ExpressionsServerStart } from './plugin'; // Kibana Platform. export { ExpressionsServerPlugin as Plugin }; -export * from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { return new ExpressionsServerPlugin(initializerContext); } From fb89b868a5e8634d4316fdff43b361843ed89fcb Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 15 Mar 2022 11:00:30 +0200 Subject: [PATCH 09/10] [Cases] Close alerts if closure options is set to automatic (#127494) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../plugins/cases/server/client/cases/push.ts | 39 ++++++- .../tests/trial/cases/push_case.ts | 105 +++++++++++++++++- 2 files changed, 142 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/cases/server/client/cases/push.ts b/x-pack/plugins/cases/server/client/cases/push.ts index 06b0922cacc47..b19a5ebdb0e5c 100644 --- a/x-pack/plugins/cases/server/client/cases/push.ts +++ b/x-pack/plugins/cases/server/client/cases/push.ts @@ -6,6 +6,7 @@ */ import Boom from '@hapi/boom'; +import { nodeBuilder } from '@kbn/es-query'; import { SavedObjectsFindResponse } from 'kibana/server'; import { @@ -17,11 +18,18 @@ import { CasesConfigureAttributes, ActionTypes, OWNER_FIELD, + CommentType, + CommentRequestAlertType, } from '../../../common/api'; +import { CASE_COMMENT_SAVED_OBJECT } from '../../../common/constants'; import { createIncident, getCommentContextFromAttributes } from './utils'; import { createCaseError } from '../../common/error'; -import { flattenCaseSavedObject, getAlertInfoFromComments } from '../../common/utils'; +import { + createAlertUpdateRequest, + flattenCaseSavedObject, + getAlertInfoFromComments, +} from '../../common/utils'; import { CasesClient, CasesClientArgs, CasesClientInternal } from '..'; import { Operations } from '../../authorization'; import { casesConnectors } from '../../connectors'; @@ -40,6 +48,30 @@ function shouldCloseByPush( ); } +const changeAlertsStatusToClose = async ( + caseId: string, + caseService: CasesClientArgs['caseService'], + alertsService: CasesClientArgs['alertsService'] +) => { + const alertAttachments = (await caseService.getAllCaseComments({ + id: [caseId], + options: { + filter: nodeBuilder.is(`${CASE_COMMENT_SAVED_OBJECT}.attributes.type`, CommentType.alert), + }, + })) as SavedObjectsFindResponse; + + const alerts = alertAttachments.saved_objects + .map((attachment) => + createAlertUpdateRequest({ + comment: attachment.attributes, + status: CaseStatuses.closed, + }) + ) + .flat(); + + await alertsService.updateAlertsStatus(alerts); +}; + /** * Parameters for pushing a case to an external system */ @@ -71,6 +103,7 @@ export const push = async ( caseService, caseConfigureService, userActionService, + alertsService, actionsClient, user, logger, @@ -224,6 +257,10 @@ export const push = async ( caseId, owner: myCase.attributes.owner, }); + + if (myCase.attributes.settings.syncAlerts) { + await changeAlertsStatusToClose(myCase.id, caseService, alertsService); + } } await userActionService.createUserAction({ diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/cases/push_case.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/cases/push_case.ts index 2eb066d110f51..193621a422726 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/cases/push_case.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/cases/push_case.ts @@ -34,8 +34,13 @@ import { getCase, getServiceNowSimulationServer, createConfiguration, + getSignalsWithES, } from '../../../../common/lib/utils'; -import { CaseConnector, CaseStatuses } from '../../../../../../plugins/cases/common/api'; +import { + CaseConnector, + CaseStatuses, + CommentType, +} from '../../../../../../plugins/cases/common/api'; import { globalRead, noKibanaPrivileges, @@ -50,6 +55,7 @@ import { export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const es = getService('es'); + const esArchiver = getService('esArchiver'); describe('push_case', () => { const actionsRemover = new ActionsRemover(supertest); @@ -295,6 +301,103 @@ export default ({ getService }: FtrProviderContext): void => { }); }); + describe('alerts', () => { + const defaultSignalsIndex = '.siem-signals-default-000001'; + const signalID = '4679431ee0ba3209b6fcd60a255a696886fe0a7d18f5375de510ff5b68fa6b78'; + const signalID2 = '1023bcfea939643c5e51fd8df53797e0ea693cee547db579ab56d96402365c1e'; + + beforeEach(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/cases/signals/default'); + }); + + afterEach(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/cases/signals/default'); + await deleteAllCaseItems(es); + }); + + const attachAlertsAndPush = async ({ syncAlerts = true }: { syncAlerts?: boolean } = {}) => { + const { postedCase, connector } = await createCaseWithConnector({ + createCaseReq: { ...getPostCaseRequest(), settings: { syncAlerts } }, + configureReq: { + closure_type: 'close-by-pushing', + }, + supertest, + serviceNowSimulatorURL, + actionsRemover, + }); + + await createComment({ + supertest, + caseId: postedCase.id, + params: { + alertId: signalID, + index: defaultSignalsIndex, + rule: { id: 'test-rule-id', name: 'test-index-id' }, + type: CommentType.alert, + owner: 'securitySolutionFixture', + }, + }); + + await createComment({ + supertest, + caseId: postedCase.id, + params: { + alertId: signalID2, + index: defaultSignalsIndex, + rule: { id: 'test-rule-id', name: 'test-index-id' }, + type: CommentType.alert, + owner: 'securitySolutionFixture', + }, + }); + + await pushCase({ + supertest, + caseId: postedCase.id, + connectorId: connector.id, + }); + + await es.indices.refresh({ index: defaultSignalsIndex }); + + const signals = await getSignalsWithES({ + es, + indices: defaultSignalsIndex, + ids: [signalID, signalID2], + }); + + return signals; + }; + + it('should change the status of all alerts attached to a case to closed when closure_type: close-by-pushing and syncAlerts: true', async () => { + const signals = await attachAlertsAndPush(); + /** + * The status of the alerts should be changed to closed when pushing a case and the + * closure_type is set to close-by-pushing + */ + expect(signals.get(defaultSignalsIndex)?.get(signalID)?._source?.signal?.status).to.be( + CaseStatuses.closed + ); + + expect(signals.get(defaultSignalsIndex)?.get(signalID2)?._source?.signal?.status).to.be( + CaseStatuses.closed + ); + }); + + it('should NOT change the status of all alerts attached to a case to closed when closure_type: close-by-pushing and syncAlerts: false', async () => { + const signals = await attachAlertsAndPush({ syncAlerts: false }); + /** + * The status of the alerts should NOT be changed to closed when pushing a case and the + * closure_type is set to close-by-pushing and syncAlert is set to false + */ + expect(signals.get(defaultSignalsIndex)?.get(signalID)?._source?.signal?.status).to.be( + CaseStatuses.open + ); + + expect(signals.get(defaultSignalsIndex)?.get(signalID2)?._source?.signal?.status).to.be( + CaseStatuses.open + ); + }); + }); + describe('rbac', () => { const supertestWithoutAuth = getService('supertestWithoutAuth'); From 53420d8658827cf20c4f06a64caaa80133d5b2c2 Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Tue, 15 Mar 2022 10:41:07 +0100 Subject: [PATCH 10/10] Get rid of `axios` dependency in the Upgrade Assistant tests. (#127122) --- .../client_integration/app/app.helpers.tsx | 11 +- .../app/cluster_upgrade.test.tsx | 19 +- .../es_deprecation_logs.helpers.ts | 6 +- .../es_deprecation_logs.test.tsx | 47 +-- .../default_deprecation_flyout.test.ts | 18 +- .../es_deprecations/deprecations_list.test.ts | 48 +-- .../es_deprecations/error_handling.test.ts | 18 +- .../es_deprecations.helpers.ts | 6 +- .../index_settings_deprecation_flyout.test.ts | 51 +-- .../ml_snapshots_deprecation_flyout.test.ts | 71 +++-- .../es_deprecations/mocked_responses.ts | 2 +- .../reindex_deprecation_flyout.test.ts | 50 +-- .../helpers/http_requests.ts | 297 +++++++----------- .../helpers/setup_environment.tsx | 17 +- .../deprecation_details_flyout.test.ts | 8 +- .../deprecations_table.test.ts | 12 +- .../deprecations_table/error_handling.test.ts | 11 +- .../kibana_deprecations.helpers.ts | 4 +- .../overview/backup_step/backup_step.test.tsx | 26 +- .../elasticsearch_deprecation_issues.test.tsx | 20 +- .../fix_issues_step/fix_issues_step.test.tsx | 14 +- .../kibana_deprecation_issues.test.tsx | 14 +- .../migrate_system_indices/flyout.test.ts | 14 +- .../migrate_system_indices.test.tsx | 26 +- .../step_completion.test.ts | 16 +- .../overview/overview.helpers.ts | 9 +- .../overview/overview.test.tsx | 8 +- .../upgrade_step/upgrade_step.test.tsx | 43 +-- 28 files changed, 409 insertions(+), 477 deletions(-) diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/app.helpers.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/app.helpers.tsx index 25e38b4111e50..ba09db95ee883 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/app.helpers.tsx +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/app.helpers.tsx @@ -8,6 +8,7 @@ import { act } from 'react-dom/test-utils'; import { registerTestBed, TestBed, AsyncTestBedConfig } from '@kbn/test-jest-helpers'; +import { HttpSetup } from 'src/core/public'; import { App } from '../../../public/application/app'; import { WithAppDependencies } from '../helpers'; @@ -39,8 +40,14 @@ const createActions = (testBed: TestBed) => { }; }; -export const setupAppPage = async (overrides?: Record): Promise => { - const initTestBed = registerTestBed(WithAppDependencies(App, overrides), testBedConfig); +export const setupAppPage = async ( + httpSetup: HttpSetup, + overrides?: Record +): Promise => { + const initTestBed = registerTestBed( + WithAppDependencies(App, httpSetup, overrides), + testBedConfig + ); const testBed = await initTestBed(); return { diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/cluster_upgrade.test.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/cluster_upgrade.test.tsx index 7276d005844c2..0ef228431592b 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/cluster_upgrade.test.tsx +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/app/cluster_upgrade.test.tsx @@ -12,20 +12,17 @@ import { AppTestBed, setupAppPage } from './app.helpers'; describe('Cluster upgrade', () => { let testBed: AppTestBed; - let server: ReturnType['server']; let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; - - beforeEach(() => { - ({ server, httpRequestsMockHelpers } = setupEnvironment()); - }); - - afterEach(() => { - server.restore(); + let httpSetup: ReturnType['httpSetup']; + beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; }); describe('when user is still preparing for upgrade', () => { beforeEach(async () => { - testBed = await setupAppPage(); + testBed = await setupAppPage(httpSetup); }); test('renders overview', () => { @@ -52,7 +49,7 @@ describe('Cluster upgrade', () => { }); await act(async () => { - testBed = await setupAppPage(); + testBed = await setupAppPage(httpSetup); }); }); @@ -76,7 +73,7 @@ describe('Cluster upgrade', () => { }); await act(async () => { - testBed = await setupAppPage(); + testBed = await setupAppPage(httpSetup); }); }); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecation_logs/es_deprecation_logs.helpers.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecation_logs/es_deprecation_logs.helpers.ts index 31bbcd01a7320..813a269bf5a97 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecation_logs/es_deprecation_logs.helpers.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecation_logs/es_deprecation_logs.helpers.ts @@ -7,7 +7,8 @@ import { act } from 'react-dom/test-utils'; import { registerTestBed, TestBed, AsyncTestBedConfig } from '@kbn/test-jest-helpers'; -import { EsDeprecationLogs } from '../../../public/application/components/es_deprecation_logs'; +import { HttpSetup } from 'src/core/public'; +import { EsDeprecationLogs } from '../../../public/application/components'; import { WithAppDependencies } from '../helpers'; const testBedConfig: AsyncTestBedConfig = { @@ -65,10 +66,11 @@ const createActions = (testBed: TestBed) => { }; export const setupESDeprecationLogsPage = async ( + httpSetup: HttpSetup, overrides?: Record ): Promise => { const initTestBed = registerTestBed( - WithAppDependencies(EsDeprecationLogs, overrides), + WithAppDependencies(EsDeprecationLogs, httpSetup, overrides), testBedConfig ); const testBed = await initTestBed(); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecation_logs/es_deprecation_logs.test.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecation_logs/es_deprecation_logs.test.tsx index 679a9175f5c50..f4b07c6f2f5ad 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecation_logs/es_deprecation_logs.test.tsx +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecation_logs/es_deprecation_logs.test.tsx @@ -41,18 +41,18 @@ const getLoggingResponse = (toggle: boolean): DeprecationLoggingStatus => ({ describe('ES deprecation logs', () => { let testBed: EsDeprecationLogsTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - + let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; + let httpSetup: ReturnType['httpSetup']; beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; + httpRequestsMockHelpers.setLoadDeprecationLoggingResponse(getLoggingResponse(true)); - testBed = await setupESDeprecationLogsPage(); + testBed = await setupESDeprecationLogsPage(httpSetup); testBed.component.update(); }); - afterAll(() => { - server.restore(); - }); - describe('Documentation link', () => { test('Has a link for migration info api docs in page header', () => { const { exists } = testBed; @@ -71,8 +71,11 @@ describe('ES deprecation logs', () => { await actions.clickDeprecationToggle(); - const latestRequest = server.requests[server.requests.length - 1]; - expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual({ isEnabled: false }); + expect(httpSetup.put).toHaveBeenLastCalledWith( + `/api/upgrade_assistant/deprecation_logging`, + expect.objectContaining({ body: JSON.stringify({ isEnabled: false }) }) + ); + expect(find('deprecationLoggingToggle').props()['aria-checked']).toBe(false); }); @@ -83,7 +86,7 @@ describe('ES deprecation logs', () => { }); await act(async () => { - testBed = await setupESDeprecationLogsPage(); + testBed = await setupESDeprecationLogsPage(httpSetup); }); const { exists, component } = testBed; @@ -119,7 +122,7 @@ describe('ES deprecation logs', () => { httpRequestsMockHelpers.setLoadDeprecationLoggingResponse(undefined, error); await act(async () => { - testBed = await setupESDeprecationLogsPage(); + testBed = await setupESDeprecationLogsPage(httpSetup); }); const { component, exists } = testBed; @@ -136,7 +139,7 @@ describe('ES deprecation logs', () => { }); await act(async () => { - testBed = await setupESDeprecationLogsPage(); + testBed = await setupESDeprecationLogsPage(httpSetup); }); const { exists, component } = testBed; @@ -156,7 +159,7 @@ describe('ES deprecation logs', () => { test('Has a link to see logs in observability app', async () => { await act(async () => { - testBed = await setupESDeprecationLogsPage({ + testBed = await setupESDeprecationLogsPage(httpSetup, { http: { basePath: { prepend: (url: string) => url, @@ -194,7 +197,7 @@ describe('ES deprecation logs', () => { test('Has a link to see logs in discover app', async () => { await act(async () => { - testBed = await setupESDeprecationLogsPage(); + testBed = await setupESDeprecationLogsPage(httpSetup); }); const { exists, component, find } = testBed; @@ -233,7 +236,7 @@ describe('ES deprecation logs', () => { }); await act(async () => { - testBed = await setupESDeprecationLogsPage(); + testBed = await setupESDeprecationLogsPage(httpSetup); }); const { find, exists, component } = testBed; @@ -250,7 +253,7 @@ describe('ES deprecation logs', () => { }); await act(async () => { - testBed = await setupESDeprecationLogsPage(); + testBed = await setupESDeprecationLogsPage(httpSetup); }); const { find, exists, component } = testBed; @@ -271,7 +274,7 @@ describe('ES deprecation logs', () => { httpRequestsMockHelpers.setLoadDeprecationLogsCountResponse(undefined, error); await act(async () => { - testBed = await setupESDeprecationLogsPage(); + testBed = await setupESDeprecationLogsPage(httpSetup); }); const { exists, actions, component } = testBed; @@ -295,7 +298,7 @@ describe('ES deprecation logs', () => { }); await act(async () => { - testBed = await setupESDeprecationLogsPage(); + testBed = await setupESDeprecationLogsPage(httpSetup); }); const { exists, actions, component } = testBed; @@ -327,7 +330,7 @@ describe('ES deprecation logs', () => { const addDanger = jest.fn(); await act(async () => { - testBed = await setupESDeprecationLogsPage({ + testBed = await setupESDeprecationLogsPage(httpSetup, { services: { core: { notifications: { @@ -365,7 +368,7 @@ describe('ES deprecation logs', () => { count: 0, }); - testBed = await setupESDeprecationLogsPage(); + testBed = await setupESDeprecationLogsPage(httpSetup); }); afterEach(() => { @@ -401,7 +404,7 @@ describe('ES deprecation logs', () => { test('It shows copy with compatibility api header advice', async () => { await act(async () => { - testBed = await setupESDeprecationLogsPage(); + testBed = await setupESDeprecationLogsPage(httpSetup); }); const { exists, component } = testBed; @@ -425,7 +428,7 @@ describe('ES deprecation logs', () => { test(`doesn't show analyze and resolve logs if it doesn't have the right privileges`, async () => { await act(async () => { - testBed = await setupESDeprecationLogsPage({ + testBed = await setupESDeprecationLogsPage(httpSetup, { privileges: { hasAllPrivileges: false, missingPrivileges: { diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/default_deprecation_flyout.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/default_deprecation_flyout.test.ts index 5566ec1d17e2b..f728e6685817a 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/default_deprecation_flyout.test.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/default_deprecation_flyout.test.ts @@ -13,13 +13,13 @@ import { esDeprecationsMockResponse, MOCK_SNAPSHOT_ID, MOCK_JOB_ID } from './moc describe('Default deprecation flyout', () => { let testBed: ElasticsearchTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - - afterAll(() => { - server.restore(); - }); - + let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; + let httpSetup: ReturnType['httpSetup']; beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ nodeId: 'my_node', @@ -27,7 +27,7 @@ describe('Default deprecation flyout', () => { jobId: MOCK_JOB_ID, status: 'idle', }); - httpRequestsMockHelpers.setReindexStatusResponse({ + httpRequestsMockHelpers.setReindexStatusResponse('reindex_index', { reindexOp: null, warnings: [], hasRequiredPrivileges: true, @@ -39,7 +39,9 @@ describe('Default deprecation flyout', () => { }); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { + isReadOnlyMode: false, + }); }); testBed.component.update(); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/deprecations_list.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/deprecations_list.test.ts index ca393e165e335..457c0c4ec2be5 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/deprecations_list.test.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/deprecations_list.test.ts @@ -20,13 +20,13 @@ import { describe('ES deprecations table', () => { let testBed: ElasticsearchTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - - afterAll(() => { - server.restore(); - }); - + let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; + let httpSetup: ReturnType['httpSetup']; beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ nodeId: 'my_node', @@ -34,7 +34,7 @@ describe('ES deprecations table', () => { jobId: MOCK_JOB_ID, status: 'idle', }); - httpRequestsMockHelpers.setReindexStatusResponse({ + httpRequestsMockHelpers.setReindexStatusResponse('reindex_index', { reindexOp: null, warnings: [], hasRequiredPrivileges: true, @@ -47,7 +47,7 @@ describe('ES deprecations table', () => { httpRequestsMockHelpers.setLoadRemoteClustersResponse([]); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { isReadOnlyMode: false }); }); testBed.component.update(); @@ -66,7 +66,6 @@ describe('ES deprecations table', () => { it('refreshes deprecation data', async () => { const { actions } = testBed; - const totalRequests = server.requests.length; await actions.table.clickRefreshButton(); @@ -74,21 +73,24 @@ describe('ES deprecations table', () => { const reindexDeprecation = esDeprecationsMockResponse.deprecations[3]; // Since upgradeStatusMockResponse includes ML and reindex actions (which require fetching status), there will be 4 requests made - expect(server.requests.length).toBe(totalRequests + 4); - expect(server.requests[server.requests.length - 4].url).toBe( - `${API_BASE_PATH}/es_deprecations` + expect(httpSetup.get).toHaveBeenCalledWith( + `${API_BASE_PATH}/es_deprecations`, + expect.anything() ); - expect(server.requests[server.requests.length - 3].url).toBe( + expect(httpSetup.get).toHaveBeenCalledWith( `${API_BASE_PATH}/ml_snapshots/${(mlDeprecation.correctiveAction as MlAction).jobId}/${ (mlDeprecation.correctiveAction as MlAction).snapshotId - }` + }`, + expect.anything() ); - expect(server.requests[server.requests.length - 2].url).toBe( - `${API_BASE_PATH}/reindex/${reindexDeprecation.index}` + expect(httpSetup.get).toHaveBeenCalledWith( + `${API_BASE_PATH}/reindex/${reindexDeprecation.index}`, + expect.anything() ); - expect(server.requests[server.requests.length - 1].url).toBe( - `${API_BASE_PATH}/ml_upgrade_mode` + expect(httpSetup.get).toHaveBeenCalledWith( + `${API_BASE_PATH}/ml_upgrade_mode`, + expect.anything() ); }); @@ -111,7 +113,9 @@ describe('ES deprecations table', () => { httpRequestsMockHelpers.setLoadRemoteClustersResponse(['test_remote_cluster']); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { + isReadOnlyMode: false, + }); }); testBed.component.update(); @@ -217,7 +221,9 @@ describe('ES deprecations table', () => { }); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { + isReadOnlyMode: false, + }); }); testBed.component.update(); @@ -299,7 +305,7 @@ describe('ES deprecations table', () => { httpRequestsMockHelpers.setLoadEsDeprecationsResponse(noDeprecationsResponse); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { isReadOnlyMode: false }); }); testBed.component.update(); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/error_handling.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/error_handling.test.ts index 2f0c8f0597ec3..02e61fdaaadd0 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/error_handling.test.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/error_handling.test.ts @@ -12,10 +12,12 @@ import { ElasticsearchTestBed, setupElasticsearchPage } from './es_deprecations. describe('Error handling', () => { let testBed: ElasticsearchTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - - afterAll(() => { - server.restore(); + let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; + let httpSetup: ReturnType['httpSetup']; + beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; }); it('handles 403', async () => { @@ -28,7 +30,7 @@ describe('Error handling', () => { httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { isReadOnlyMode: false }); }); const { component, find } = testBed; @@ -53,7 +55,7 @@ describe('Error handling', () => { httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { isReadOnlyMode: false }); }); const { component, find } = testBed; @@ -76,7 +78,7 @@ describe('Error handling', () => { httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { isReadOnlyMode: false }); }); const { component, find } = testBed; @@ -96,7 +98,7 @@ describe('Error handling', () => { httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { isReadOnlyMode: false }); }); const { component, find } = testBed; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/es_deprecations.helpers.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/es_deprecations.helpers.ts index 08d53d1eaca7e..02fe72883c34f 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/es_deprecations.helpers.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/es_deprecations.helpers.ts @@ -7,7 +7,8 @@ import { act } from 'react-dom/test-utils'; import { registerTestBed, TestBed, AsyncTestBedConfig } from '@kbn/test-jest-helpers'; -import { EsDeprecations } from '../../../public/application/components/es_deprecations'; +import { HttpSetup } from 'src/core/public'; +import { EsDeprecations } from '../../../public/application/components'; import { WithAppDependencies } from '../helpers'; const testBedConfig: AsyncTestBedConfig = { @@ -146,10 +147,11 @@ const createActions = (testBed: TestBed) => { }; export const setupElasticsearchPage = async ( + httpSetup: HttpSetup, overrides?: Record ): Promise => { const initTestBed = registerTestBed( - WithAppDependencies(EsDeprecations, overrides), + WithAppDependencies(EsDeprecations, httpSetup, overrides), testBedConfig ); const testBed = await initTestBed(); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/index_settings_deprecation_flyout.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/index_settings_deprecation_flyout.test.ts index f032c34040bfe..20b7bed032f76 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/index_settings_deprecation_flyout.test.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/index_settings_deprecation_flyout.test.ts @@ -9,18 +9,23 @@ import { act } from 'react-dom/test-utils'; import { setupEnvironment } from '../helpers'; import { ElasticsearchTestBed, setupElasticsearchPage } from './es_deprecations.helpers'; -import { esDeprecationsMockResponse, MOCK_SNAPSHOT_ID, MOCK_JOB_ID } from './mocked_responses'; +import { + esDeprecationsMockResponse, + MOCK_SNAPSHOT_ID, + MOCK_JOB_ID, + MOCK_REINDEX_DEPRECATION, +} from './mocked_responses'; describe('Index settings deprecation flyout', () => { let testBed: ElasticsearchTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); + let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; + let httpSetup: ReturnType['httpSetup']; const indexSettingDeprecation = esDeprecationsMockResponse.deprecations[1]; - - afterAll(() => { - server.restore(); - }); - beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ nodeId: 'my_node', @@ -28,7 +33,7 @@ describe('Index settings deprecation flyout', () => { jobId: MOCK_JOB_ID, status: 'idle', }); - httpRequestsMockHelpers.setReindexStatusResponse({ + httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { reindexOp: null, warnings: [], hasRequiredPrivileges: true, @@ -40,7 +45,7 @@ describe('Index settings deprecation flyout', () => { }); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { isReadOnlyMode: false }); }); const { actions, component } = testBed; @@ -48,7 +53,7 @@ describe('Index settings deprecation flyout', () => { await actions.table.clickDeprecationRowAt('indexSetting', 0); }); - test('renders a flyout with deprecation details', async () => { + it('renders a flyout with deprecation details', async () => { const { find, exists } = testBed; expect(exists('indexSettingsDetails')).toBe(true); @@ -64,7 +69,7 @@ describe('Index settings deprecation flyout', () => { it('removes deprecated index settings', async () => { const { find, actions, exists } = testBed; - httpRequestsMockHelpers.setUpdateIndexSettingsResponse({ + httpRequestsMockHelpers.setUpdateIndexSettingsResponse(indexSettingDeprecation.index!, { acknowledged: true, }); @@ -72,13 +77,10 @@ describe('Index settings deprecation flyout', () => { await actions.indexSettingsDeprecationFlyout.clickDeleteSettingsButton(); - const request = server.requests[server.requests.length - 1]; - - expect(request.method).toBe('POST'); - expect(request.url).toBe( - `/api/upgrade_assistant/${indexSettingDeprecation.index!}/index_settings` + expect(httpSetup.post).toHaveBeenLastCalledWith( + `/api/upgrade_assistant/${indexSettingDeprecation.index!}/index_settings`, + expect.anything() ); - expect(request.status).toEqual(200); // Verify the "Resolution" column of the table is updated expect(find('indexSettingsResolutionStatusCell').at(0).text()).toEqual( @@ -104,17 +106,18 @@ describe('Index settings deprecation flyout', () => { message: 'Remove index settings error', }; - httpRequestsMockHelpers.setUpdateIndexSettingsResponse(undefined, error); + httpRequestsMockHelpers.setUpdateIndexSettingsResponse( + indexSettingDeprecation.index!, + undefined, + error + ); await actions.indexSettingsDeprecationFlyout.clickDeleteSettingsButton(); - const request = server.requests[server.requests.length - 1]; - - expect(request.method).toBe('POST'); - expect(request.url).toBe( - `/api/upgrade_assistant/${indexSettingDeprecation.index!}/index_settings` + expect(httpSetup.post).toHaveBeenLastCalledWith( + `/api/upgrade_assistant/${indexSettingDeprecation.index!}/index_settings`, + expect.anything() ); - expect(request.status).toEqual(500); // Verify the "Resolution" column of the table is updated expect(find('indexSettingsResolutionStatusCell').at(0).text()).toEqual( diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/ml_snapshots_deprecation_flyout.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/ml_snapshots_deprecation_flyout.test.ts index 11bb27bb8b331..2fa645a60d6d5 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/ml_snapshots_deprecation_flyout.test.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/ml_snapshots_deprecation_flyout.test.ts @@ -14,14 +14,14 @@ import { esDeprecationsMockResponse, MOCK_SNAPSHOT_ID, MOCK_JOB_ID } from './moc describe('Machine learning deprecation flyout', () => { let testBed: ElasticsearchTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); const mlDeprecation = esDeprecationsMockResponse.deprecations[0]; - - afterAll(() => { - server.restore(); - }); - + let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; + let httpSetup: ReturnType['httpSetup']; beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); httpRequestsMockHelpers.setLoadMlUpgradeModeResponse({ mlUpgradeModeEnabled: false }); httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ @@ -30,7 +30,7 @@ describe('Machine learning deprecation flyout', () => { jobId: MOCK_JOB_ID, status: 'idle', }); - httpRequestsMockHelpers.setReindexStatusResponse({ + httpRequestsMockHelpers.setReindexStatusResponse('reindex_index', { reindexOp: null, warnings: [], hasRequiredPrivileges: true, @@ -42,7 +42,7 @@ describe('Machine learning deprecation flyout', () => { }); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(mockEnvironment.httpSetup, { isReadOnlyMode: false }); }); const { actions, component } = testBed; @@ -84,15 +84,15 @@ describe('Machine learning deprecation flyout', () => { await actions.mlDeprecationFlyout.clickUpgradeSnapshot(); // First, we expect a POST request to upgrade the snapshot - const upgradeRequest = server.requests[server.requests.length - 2]; - expect(upgradeRequest.method).toBe('POST'); - expect(upgradeRequest.url).toBe('/api/upgrade_assistant/ml_snapshots'); + expect(httpSetup.post).toHaveBeenLastCalledWith( + '/api/upgrade_assistant/ml_snapshots', + expect.anything() + ); // Next, we expect a GET request to check the status of the upgrade - const statusRequest = server.requests[server.requests.length - 1]; - expect(statusRequest.method).toBe('GET'); - expect(statusRequest.url).toBe( - `/api/upgrade_assistant/ml_snapshots/${MOCK_JOB_ID}/${MOCK_SNAPSHOT_ID}` + expect(httpSetup.get).toHaveBeenLastCalledWith( + `/api/upgrade_assistant/ml_snapshots/${MOCK_JOB_ID}/${MOCK_SNAPSHOT_ID}`, + expect.anything() ); // Verify the "Resolution" column of the table is updated @@ -128,9 +128,10 @@ describe('Machine learning deprecation flyout', () => { await actions.mlDeprecationFlyout.clickUpgradeSnapshot(); - const upgradeRequest = server.requests[server.requests.length - 1]; - expect(upgradeRequest.method).toBe('POST'); - expect(upgradeRequest.url).toBe('/api/upgrade_assistant/ml_snapshots'); + expect(httpSetup.post).toHaveBeenLastCalledWith( + '/api/upgrade_assistant/ml_snapshots', + expect.anything() + ); // Verify the "Resolution" column of the table is updated expect(find('mlActionResolutionCell').text()).toContain('Upgrade failed'); @@ -147,10 +148,12 @@ describe('Machine learning deprecation flyout', () => { }); it('Disables actions if ml_upgrade_mode is enabled', async () => { - httpRequestsMockHelpers.setLoadMlUpgradeModeResponse({ mlUpgradeModeEnabled: true }); + httpRequestsMockHelpers.setLoadMlUpgradeModeResponse({ + mlUpgradeModeEnabled: true, + }); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { isReadOnlyMode: false }); }); const { actions, exists, component } = testBed; @@ -172,7 +175,9 @@ describe('Machine learning deprecation flyout', () => { it('successfully deletes snapshots', async () => { const { find, actions, exists } = testBed; - httpRequestsMockHelpers.setDeleteMlSnapshotResponse({ + const jobId = (mlDeprecation.correctiveAction! as MlAction).jobId; + const snapshotId = (mlDeprecation.correctiveAction! as MlAction).snapshotId; + httpRequestsMockHelpers.setDeleteMlSnapshotResponse(jobId, snapshotId, { acknowledged: true, }); @@ -181,13 +186,9 @@ describe('Machine learning deprecation flyout', () => { await actions.mlDeprecationFlyout.clickDeleteSnapshot(); - const request = server.requests[server.requests.length - 1]; - - expect(request.method).toBe('DELETE'); - expect(request.url).toBe( - `/api/upgrade_assistant/ml_snapshots/${ - (mlDeprecation.correctiveAction! as MlAction).jobId - }/${(mlDeprecation.correctiveAction! as MlAction).snapshotId}` + expect(httpSetup.delete).toHaveBeenLastCalledWith( + `/api/upgrade_assistant/ml_snapshots/${jobId}/${snapshotId}`, + expect.anything() ); // Verify the "Resolution" column of the table is updated @@ -212,17 +213,15 @@ describe('Machine learning deprecation flyout', () => { message: 'Upgrade snapshot error', }; - httpRequestsMockHelpers.setDeleteMlSnapshotResponse(undefined, error); + const jobId = (mlDeprecation.correctiveAction! as MlAction).jobId; + const snapshotId = (mlDeprecation.correctiveAction! as MlAction).snapshotId; + httpRequestsMockHelpers.setDeleteMlSnapshotResponse(jobId, snapshotId, undefined, error); await actions.mlDeprecationFlyout.clickDeleteSnapshot(); - const request = server.requests[server.requests.length - 1]; - - expect(request.method).toBe('DELETE'); - expect(request.url).toBe( - `/api/upgrade_assistant/ml_snapshots/${ - (mlDeprecation.correctiveAction! as MlAction).jobId - }/${(mlDeprecation.correctiveAction! as MlAction).snapshotId}` + expect(httpSetup.delete).toHaveBeenLastCalledWith( + `/api/upgrade_assistant/ml_snapshots/${jobId}/${snapshotId}`, + expect.anything() ); // Verify the "Resolution" column of the table is updated diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/mocked_responses.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/mocked_responses.ts index ddf477195063c..09f60a8dd3074 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/mocked_responses.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/mocked_responses.ts @@ -26,7 +26,7 @@ export const MOCK_ML_DEPRECATION: EnrichedDeprecationInfo = { }, }; -const MOCK_REINDEX_DEPRECATION: EnrichedDeprecationInfo = { +export const MOCK_REINDEX_DEPRECATION: EnrichedDeprecationInfo = { isCritical: true, resolveDuringUpgrade: false, type: 'index_settings', diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/reindex_deprecation_flyout.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/reindex_deprecation_flyout.test.ts index 25742958aa243..8067d3389115a 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/reindex_deprecation_flyout.test.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/reindex_deprecation_flyout.test.ts @@ -10,7 +10,12 @@ import { act } from 'react-dom/test-utils'; import { ReindexStatus, ReindexStep, ReindexStatusResponse } from '../../../common/types'; import { setupEnvironment } from '../helpers'; import { ElasticsearchTestBed, setupElasticsearchPage } from './es_deprecations.helpers'; -import { esDeprecationsMockResponse, MOCK_SNAPSHOT_ID, MOCK_JOB_ID } from './mocked_responses'; +import { + esDeprecationsMockResponse, + MOCK_SNAPSHOT_ID, + MOCK_JOB_ID, + MOCK_REINDEX_DEPRECATION, +} from './mocked_responses'; const defaultReindexStatusMeta: ReindexStatusResponse['meta'] = { indexName: 'foo', @@ -21,7 +26,6 @@ const defaultReindexStatusMeta: ReindexStatusResponse['meta'] = { // Note: The reindexing flyout UX is subject to change; more tests should be added here once functionality is built out describe('Reindex deprecation flyout', () => { let testBed: ElasticsearchTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); beforeAll(() => { jest.useFakeTimers(); @@ -29,10 +33,15 @@ describe('Reindex deprecation flyout', () => { afterAll(() => { jest.useRealTimers(); - server.restore(); }); + let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; + let httpSetup: ReturnType['httpSetup']; beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ nodeId: 'my_node', @@ -40,7 +49,7 @@ describe('Reindex deprecation flyout', () => { jobId: MOCK_JOB_ID, status: 'idle', }); - httpRequestsMockHelpers.setReindexStatusResponse({ + httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { reindexOp: null, warnings: [], hasRequiredPrivileges: true, @@ -52,7 +61,7 @@ describe('Reindex deprecation flyout', () => { }); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { isReadOnlyMode: false }); }); testBed.component.update(); @@ -71,15 +80,8 @@ describe('Reindex deprecation flyout', () => { }); it('renders error callout when reindex fails', async () => { - httpRequestsMockHelpers.setReindexStatusResponse({ - reindexOp: null, - warnings: [], - hasRequiredPrivileges: true, - meta: defaultReindexStatusMeta, - }); - await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { isReadOnlyMode: false }); }); testBed.component.update(); @@ -88,7 +90,7 @@ describe('Reindex deprecation flyout', () => { await actions.table.clickDeprecationRowAt('reindex', 0); - httpRequestsMockHelpers.setStartReindexingResponse(undefined, { + httpRequestsMockHelpers.setStartReindexingResponse(MOCK_REINDEX_DEPRECATION.index!, undefined, { statusCode: 404, message: 'no such index [test]', }); @@ -99,13 +101,13 @@ describe('Reindex deprecation flyout', () => { }); it('renders error callout when fetch status fails', async () => { - httpRequestsMockHelpers.setReindexStatusResponse(undefined, { + httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, undefined, { statusCode: 404, message: 'no such index [test]', }); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { isReadOnlyMode: false }); }); testBed.component.update(); @@ -127,7 +129,7 @@ describe('Reindex deprecation flyout', () => { }); it('has started but not yet reindexing documents', async () => { - httpRequestsMockHelpers.setReindexStatusResponse({ + httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { reindexOp: { status: ReindexStatus.inProgress, lastCompletedStep: ReindexStep.readonly, @@ -139,7 +141,7 @@ describe('Reindex deprecation flyout', () => { }); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { isReadOnlyMode: false }); }); testBed.component.update(); @@ -152,7 +154,7 @@ describe('Reindex deprecation flyout', () => { }); it('has started reindexing documents', async () => { - httpRequestsMockHelpers.setReindexStatusResponse({ + httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { reindexOp: { status: ReindexStatus.inProgress, lastCompletedStep: ReindexStep.reindexStarted, @@ -164,7 +166,7 @@ describe('Reindex deprecation flyout', () => { }); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { isReadOnlyMode: false }); }); testBed.component.update(); @@ -177,7 +179,7 @@ describe('Reindex deprecation flyout', () => { }); it('has completed reindexing documents', async () => { - httpRequestsMockHelpers.setReindexStatusResponse({ + httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { reindexOp: { status: ReindexStatus.inProgress, lastCompletedStep: ReindexStep.reindexCompleted, @@ -189,7 +191,7 @@ describe('Reindex deprecation flyout', () => { }); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { isReadOnlyMode: false }); }); testBed.component.update(); @@ -202,7 +204,7 @@ describe('Reindex deprecation flyout', () => { }); it('has completed', async () => { - httpRequestsMockHelpers.setReindexStatusResponse({ + httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { reindexOp: { status: ReindexStatus.completed, lastCompletedStep: ReindexStep.aliasCreated, @@ -214,7 +216,7 @@ describe('Reindex deprecation flyout', () => { }); await act(async () => { - testBed = await setupElasticsearchPage({ isReadOnlyMode: false }); + testBed = await setupElasticsearchPage(httpSetup, { isReadOnlyMode: false }); }); const { actions, find, exists, component } = testBed; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts index 774dfba4c1b9f..70a7efecd7183 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts @@ -5,7 +5,7 @@ * 2.0. */ -import sinon, { SinonFakeServer } from 'sinon'; +import { httpServiceMock } from '../../../../../../src/core/public/mocks'; import { API_BASE_PATH } from '../../../common/constants'; import { @@ -15,204 +15,132 @@ import { ResponseError, } from '../../../common/types'; +type HttpMethod = 'GET' | 'PUT' | 'DELETE' | 'POST'; + // Register helpers to mock HTTP Requests -const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { - const setLoadCloudBackupStatusResponse = ( - response?: CloudBackupStatus, - error?: ResponseError - ) => { - const status = error ? error.statusCode || 400 : 200; - const body = error ? error : response; +const registerHttpRequestMockHelpers = ( + httpSetup: ReturnType, + shouldDelayResponse: () => boolean +) => { + const mockResponses = new Map>>( + ['GET', 'PUT', 'DELETE', 'POST'].map( + (method) => [method, new Map()] as [HttpMethod, Map>] + ) + ); + + const mockMethodImplementation = (method: HttpMethod, path: string) => { + const responsePromise = mockResponses.get(method)?.get(path) ?? Promise.resolve({}); + if (shouldDelayResponse()) { + return new Promise((resolve) => { + setTimeout(() => resolve(responsePromise), 1000); + }); + } - server.respondWith('GET', `${API_BASE_PATH}/cloud_backup_status`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); + return responsePromise; }; - const setLoadEsDeprecationsResponse = (response?: ESUpgradeStatus, error?: ResponseError) => { - const status = error ? error.statusCode || 400 : 200; - const body = error ? error : response; + httpSetup.get.mockImplementation((path) => + mockMethodImplementation('GET', path as unknown as string) + ); + httpSetup.delete.mockImplementation((path) => + mockMethodImplementation('DELETE', path as unknown as string) + ); + httpSetup.post.mockImplementation((path) => + mockMethodImplementation('POST', path as unknown as string) + ); + httpSetup.put.mockImplementation((path) => + mockMethodImplementation('PUT', path as unknown as string) + ); - server.respondWith('GET', `${API_BASE_PATH}/es_deprecations`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); + const mockResponse = (method: HttpMethod, path: string, response?: unknown, error?: unknown) => { + const defuse = (promise: Promise) => { + promise.catch(() => {}); + return promise; + }; + + return mockResponses + .get(method)! + .set(path, error ? defuse(Promise.reject({ body: error })) : Promise.resolve(response)); }; + const setLoadCloudBackupStatusResponse = (response?: CloudBackupStatus, error?: ResponseError) => + mockResponse('GET', `${API_BASE_PATH}/cloud_backup_status`, response, error); + + const setLoadEsDeprecationsResponse = (response?: ESUpgradeStatus, error?: ResponseError) => + mockResponse('GET', `${API_BASE_PATH}/es_deprecations`, response, error); + const setLoadDeprecationLoggingResponse = ( response?: DeprecationLoggingStatus, error?: ResponseError - ) => { - const status = error ? error.statusCode || 400 : 200; - const body = error ? error : response; - - server.respondWith('GET', `${API_BASE_PATH}/deprecation_logging`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); - }; + ) => mockResponse('GET', `${API_BASE_PATH}/deprecation_logging`, response, error); const setLoadDeprecationLogsCountResponse = ( response?: { count: number }, error?: ResponseError - ) => { - const status = error ? error.statusCode || 400 : 200; - const body = error ? error : response; - - server.respondWith('GET', `${API_BASE_PATH}/deprecation_logging/count`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); - }; + ) => mockResponse('GET', `${API_BASE_PATH}/deprecation_logging/count`, response, error); - const setDeleteLogsCacheResponse = (response?: string, error?: ResponseError) => { - const status = error ? error.statusCode || 400 : 200; - const body = error ? error : response; - server.respondWith('DELETE', `${API_BASE_PATH}/deprecation_logging/cache`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); - }; + const setDeleteLogsCacheResponse = (response?: string, error?: ResponseError) => + mockResponse('DELETE', `${API_BASE_PATH}/deprecation_logging/cache`, response, error); const setUpdateDeprecationLoggingResponse = ( response?: DeprecationLoggingStatus, error?: ResponseError - ) => { - const status = error ? error.statusCode || 400 : 200; - const body = error ? error : response; + ) => mockResponse('PUT', `${API_BASE_PATH}/deprecation_logging`, response, error); - server.respondWith('PUT', `${API_BASE_PATH}/deprecation_logging`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); - }; - - const setUpdateIndexSettingsResponse = (response?: object, error?: ResponseError) => { - const status = error ? error.statusCode || 400 : 200; - const body = error ? error : response; - server.respondWith('POST', `${API_BASE_PATH}/:indexName/index_settings`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); - }; - - const setUpgradeMlSnapshotResponse = (response?: object, error?: ResponseError) => { - const status = error ? error.statusCode || 400 : 200; - const body = error ? error : response; - - server.respondWith('POST', `${API_BASE_PATH}/ml_snapshots`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); - }; - - const setUpgradeMlSnapshotStatusResponse = (response?: object, error?: ResponseError) => { - const status = error ? error.statusCode || 400 : 200; - const body = error ? error : response; - - server.respondWith('GET', `${API_BASE_PATH}/ml_snapshots/:jobId/:snapshotId`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); - }; - - const setReindexStatusResponse = (response?: object, error?: ResponseError) => { - const status = error ? error.statusCode || 400 : 200; - const body = error ? error : response; - - server.respondWith('GET', `${API_BASE_PATH}/reindex/:indexName`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); - }; - - const setStartReindexingResponse = (response?: object, error?: ResponseError) => { - const status = error ? error.statusCode || 400 : 200; - const body = error ? error : response; - - server.respondWith('POST', `${API_BASE_PATH}/reindex/:indexName`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); - }; - - const setDeleteMlSnapshotResponse = (response?: object, error?: ResponseError) => { - const status = error ? error.statusCode || 400 : 200; - const body = error ? error : response; - - server.respondWith('DELETE', `${API_BASE_PATH}/ml_snapshots/:jobId/:snapshotId`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); - }; - - const setLoadSystemIndicesMigrationStatus = (response?: object, error?: ResponseError) => { - const status = error ? error.statusCode || 400 : 200; - const body = error ? error : response; + const setUpdateIndexSettingsResponse = ( + indexName: string, + response?: object, + error?: ResponseError + ) => mockResponse('POST', `${API_BASE_PATH}/${indexName}/index_settings`, response, error); - server.respondWith('GET', `${API_BASE_PATH}/system_indices_migration`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); - }; + const setUpgradeMlSnapshotResponse = (response?: object, error?: ResponseError) => + mockResponse('POST', `${API_BASE_PATH}/ml_snapshots`, response, error); - const setLoadMlUpgradeModeResponse = (response?: object, error?: ResponseError) => { - const status = error ? error.statusCode || 400 : 200; - const body = error ? error : response; + const setUpgradeMlSnapshotStatusResponse = ( + response?: Record, + error?: ResponseError + ) => + mockResponse( + 'GET', + `${API_BASE_PATH}/ml_snapshots/${response?.jobId}/${response?.snapshotId}`, + response, + error + ); + + const setReindexStatusResponse = ( + indexName: string, + response?: Record, + error?: ResponseError + ) => mockResponse('GET', `${API_BASE_PATH}/reindex/${indexName}`, response, error); - server.respondWith('GET', `${API_BASE_PATH}/ml_upgrade_mode`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); - }; + const setStartReindexingResponse = ( + indexName: string, + response?: object, + error?: ResponseError + ) => mockResponse('POST', `${API_BASE_PATH}/reindex/${indexName}`, response, error); - const setSystemIndicesMigrationResponse = (response?: object, error?: ResponseError) => { - const status = error ? error.statusCode || 400 : 200; - const body = error ? error : response; + const setDeleteMlSnapshotResponse = ( + jobId: string, + snapshotId: string, + response?: object, + error?: ResponseError + ) => + mockResponse('DELETE', `${API_BASE_PATH}/ml_snapshots/${jobId}/${snapshotId}`, response, error); - server.respondWith('POST', `${API_BASE_PATH}/system_indices_migration`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); - }; + const setLoadSystemIndicesMigrationStatus = (response?: object, error?: ResponseError) => + mockResponse('GET', `${API_BASE_PATH}/system_indices_migration`, response, error); - const setGetUpgradeStatusResponse = (response?: object, error?: ResponseError) => { - const status = error ? error.statusCode || 400 : 200; - const body = error ? error : response; + const setLoadMlUpgradeModeResponse = (response?: object, error?: ResponseError) => + mockResponse('GET', `${API_BASE_PATH}/ml_upgrade_mode`, response, error); - server.respondWith('GET', `${API_BASE_PATH}/status`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); - }; + const setSystemIndicesMigrationResponse = (response?: object, error?: ResponseError) => + mockResponse('POST', `${API_BASE_PATH}/system_indices_migration`, response, error); - const setLoadRemoteClustersResponse = (response?: object, error?: ResponseError) => { - const status = error ? error.statusCode || 400 : 200; - const body = error ? error : response; + const setGetUpgradeStatusResponse = (response?: object, error?: ResponseError) => + mockResponse('GET', `${API_BASE_PATH}/status`, response, error); - server.respondWith('GET', `${API_BASE_PATH}/remote_clusters`, [ - status, - { 'Content-Type': 'application/json' }, - JSON.stringify(body), - ]); - }; + const setLoadRemoteClustersResponse = (response?: object, error?: ResponseError) => + mockResponse('GET', `${API_BASE_PATH}/remote_clusters`, response, error); return { setLoadCloudBackupStatusResponse, @@ -236,29 +164,18 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { }; export const init = () => { - const server = sinon.fakeServer.create(); - server.respondImmediately = true; - - // Define default response for unhandled requests. - // We make requests to APIs which don't impact the component under test, e.g. UI metric telemetry, - // and we can mock them all with a 200 instead of mocking each one individually. - server.respondWith([200, {}, 'DefaultMockedResponse']); - - const httpRequestsMockHelpers = registerHttpRequestMockHelpers(server); - - const setServerAsync = (isAsync: boolean, timeout: number = 200) => { - if (isAsync) { - server.autoRespond = true; - server.autoRespondAfter = 1000; - server.respondImmediately = false; - } else { - server.respondImmediately = true; - } + let isResponseDelayed = false; + const getDelayResponse = () => isResponseDelayed; + const setDelayResponse = (shouldDelayResponse: boolean) => { + isResponseDelayed = shouldDelayResponse; }; + const httpSetup = httpServiceMock.createSetupContract(); + const httpRequestsMockHelpers = registerHttpRequestMockHelpers(httpSetup, getDelayResponse); + return { - server, - setServerAsync, + setDelayResponse, + httpSetup, httpRequestsMockHelpers, }; }; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/setup_environment.tsx index 0e4af0b697a49..7bc6931550a3b 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/setup_environment.tsx @@ -6,11 +6,8 @@ */ import React from 'react'; -import axios from 'axios'; import SemVer from 'semver/classes/semver'; import { merge } from 'lodash'; -// @ts-ignore -import axiosXhrAdapter from 'axios/lib/adapters/xhr'; import { HttpSetup } from 'src/core/public'; import { MAJOR_VERSION } from '../../../common/constants'; @@ -26,8 +23,6 @@ import { init as initHttpRequests } from './http_requests'; const { GlobalFlyoutProvider } = GlobalFlyout; -const mockHttpClient = axios.create({ adapter: axiosXhrAdapter }); - export const kibanaVersion = new SemVer(MAJOR_VERSION); const createAuthorizationContextValue = (privileges: Privileges) => { @@ -38,9 +33,9 @@ const createAuthorizationContextValue = (privileges: Privileges) => { }; export const WithAppDependencies = - (Comp: any, { privileges, ...overrides }: Record = {}) => + (Comp: any, httpSetup: HttpSetup, { privileges, ...overrides }: Record = {}) => (props: Record) => { - apiService.setup(mockHttpClient as unknown as HttpSetup); + apiService.setup(httpSetup); breadcrumbService.setup(() => ''); const appContextMock = getAppContextMock(kibanaVersion) as unknown as AppDependencies; @@ -59,11 +54,5 @@ export const WithAppDependencies = }; export const setupEnvironment = () => { - const { server, setServerAsync, httpRequestsMockHelpers } = initHttpRequests(); - - return { - server, - setServerAsync, - httpRequestsMockHelpers, - }; + return initHttpRequests(); }; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/deprecation_details_flyout/deprecation_details_flyout.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/deprecation_details_flyout/deprecation_details_flyout.test.ts index 9677104a6e558..e235bdc6f4543 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/deprecation_details_flyout/deprecation_details_flyout.test.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/deprecation_details_flyout/deprecation_details_flyout.test.ts @@ -14,21 +14,15 @@ import { KibanaTestBed, setupKibanaPage } from '../kibana_deprecations.helpers'; describe('Kibana deprecations - Deprecation details flyout', () => { let testBed: KibanaTestBed; - const { server } = setupEnvironment(); const { defaultMockedResponses: { mockedKibanaDeprecations }, } = kibanaDeprecationsServiceHelpers; const deprecationService = deprecationsServiceMock.createStartContract(); - - afterAll(() => { - server.restore(); - }); - beforeEach(async () => { await act(async () => { kibanaDeprecationsServiceHelpers.setLoadDeprecations({ deprecationService }); - testBed = await setupKibanaPage({ + testBed = await setupKibanaPage(setupEnvironment().httpSetup, { services: { core: { deprecations: deprecationService, diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/deprecations_table/deprecations_table.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/deprecations_table/deprecations_table.test.ts index a14d6e087b017..6e63d150c09f8 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/deprecations_table/deprecations_table.test.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/deprecations_table/deprecations_table.test.ts @@ -17,7 +17,6 @@ describe('Kibana deprecations - Deprecations table', () => { let testBed: KibanaTestBed; let deprecationService: jest.Mocked; - const { server } = setupEnvironment(); const { mockedKibanaDeprecations, mockedCriticalKibanaDeprecations, @@ -25,17 +24,16 @@ describe('Kibana deprecations - Deprecations table', () => { mockedConfigKibanaDeprecations, } = kibanaDeprecationsServiceHelpers.defaultMockedResponses; - afterAll(() => { - server.restore(); - }); - + let httpSetup: ReturnType['httpSetup']; beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpSetup = mockEnvironment.httpSetup; deprecationService = deprecationsServiceMock.createStartContract(); await act(async () => { kibanaDeprecationsServiceHelpers.setLoadDeprecations({ deprecationService }); - testBed = await setupKibanaPage({ + testBed = await setupKibanaPage(httpSetup, { services: { core: { deprecations: deprecationService, @@ -108,7 +106,7 @@ describe('Kibana deprecations - Deprecations table', () => { describe('No deprecations', () => { beforeEach(async () => { await act(async () => { - testBed = await setupKibanaPage({ isReadOnlyMode: false }); + testBed = await setupKibanaPage(httpSetup, { isReadOnlyMode: false }); }); const { component } = testBed; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/deprecations_table/error_handling.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/deprecations_table/error_handling.test.ts index 918ee759a0f45..13616286552ef 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/deprecations_table/error_handling.test.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/deprecations_table/error_handling.test.ts @@ -14,11 +14,12 @@ import { KibanaTestBed, setupKibanaPage } from '../kibana_deprecations.helpers'; describe('Kibana deprecations - Deprecations table - Error handling', () => { let testBed: KibanaTestBed; - const { server } = setupEnvironment(); const deprecationService = deprecationsServiceMock.createStartContract(); - afterAll(() => { - server.restore(); + let httpSetup: ReturnType['httpSetup']; + beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpSetup = mockEnvironment.httpSetup; }); test('handles plugin errors', async () => { @@ -57,7 +58,7 @@ describe('Kibana deprecations - Deprecations table - Error handling', () => { ], }); - testBed = await setupKibanaPage({ + testBed = await setupKibanaPage(httpSetup, { services: { core: { deprecations: deprecationService, @@ -83,7 +84,7 @@ describe('Kibana deprecations - Deprecations table - Error handling', () => { mockRequestErrorMessage: 'Internal Server Error', }); - testBed = await setupKibanaPage({ + testBed = await setupKibanaPage(httpSetup, { services: { core: { deprecations: deprecationService, diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/kibana_deprecations.helpers.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/kibana_deprecations.helpers.ts index dba077fc303d2..bde2a78b13302 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/kibana_deprecations.helpers.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/kibana_deprecations.helpers.ts @@ -11,6 +11,7 @@ import { AsyncTestBedConfig, findTestSubject, } from '@kbn/test-jest-helpers'; +import { HttpSetup } from 'src/core/public'; import { KibanaDeprecations } from '../../../public/application/components'; import { WithAppDependencies } from '../helpers'; @@ -118,10 +119,11 @@ const createActions = (testBed: TestBed) => { }; export const setupKibanaPage = async ( + httpSetup: HttpSetup, overrides?: Record ): Promise => { const initTestBed = registerTestBed( - WithAppDependencies(KibanaDeprecations, overrides), + WithAppDependencies(KibanaDeprecations, httpSetup, overrides), testBedConfig ); const testBed = await initTestBed(); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/backup_step/backup_step.test.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/backup_step/backup_step.test.tsx index 4cd4bf3f76629..688e060705ee4 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/backup_step/backup_step.test.tsx +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/backup_step/backup_step.test.tsx @@ -14,21 +14,19 @@ import { OverviewTestBed, setupOverviewPage } from '../overview.helpers'; describe('Overview - Backup Step', () => { let testBed: OverviewTestBed; - let server: ReturnType['server']; - let setServerAsync: ReturnType['setServerAsync']; let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; - - beforeEach(() => { - ({ server, setServerAsync, httpRequestsMockHelpers } = setupEnvironment()); - }); - - afterEach(() => { - server.restore(); + let httpSetup: ReturnType['httpSetup']; + let setDelayResponse: ReturnType['setDelayResponse']; + beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; + setDelayResponse = mockEnvironment.setDelayResponse; }); describe('On-prem', () => { beforeEach(async () => { - testBed = await setupOverviewPage(); + testBed = await setupOverviewPage(httpSetup); }); test('Shows link to Snapshot and Restore', () => { @@ -45,7 +43,7 @@ describe('Overview - Backup Step', () => { describe('On Cloud', () => { const setupCloudOverviewPage = async () => - setupOverviewPage({ + setupOverviewPage(httpSetup, { plugins: { cloud: { isCloudEnabled: true, @@ -57,14 +55,10 @@ describe('Overview - Backup Step', () => { describe('initial loading state', () => { beforeEach(async () => { // We don't want the request to load backup status to resolve immediately. - setServerAsync(true); + setDelayResponse(true); testBed = await setupCloudOverviewPage(); }); - afterEach(() => { - setServerAsync(false); - }); - test('is rendered', () => { const { exists } = testBed; expect(exists('cloudBackupLoading')).toBe(true); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_issues_step/elasticsearch_deprecation_issues.test.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_issues_step/elasticsearch_deprecation_issues.test.tsx index e1cef64dfb20c..8671b136844ed 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_issues_step/elasticsearch_deprecation_issues.test.tsx +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_issues_step/elasticsearch_deprecation_issues.test.tsx @@ -19,10 +19,12 @@ import { describe('Overview - Fix deprecation issues step - Elasticsearch deprecations', () => { let testBed: OverviewTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - - afterAll(() => { - server.restore(); + let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; + let httpSetup: ReturnType['httpSetup']; + beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; }); describe('When load succeeds', () => { @@ -32,7 +34,7 @@ describe('Overview - Fix deprecation issues step - Elasticsearch deprecations', const deprecationService = deprecationsServiceMock.createStartContract(); kibanaDeprecationsServiceHelpers.setLoadDeprecations({ deprecationService, response: [] }); - testBed = await setupOverviewPage({ + testBed = await setupOverviewPage(httpSetup, { services: { core: { deprecations: deprecationService, @@ -116,7 +118,7 @@ describe('Overview - Fix deprecation issues step - Elasticsearch deprecations', httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); await act(async () => { - testBed = await setupOverviewPage(); + testBed = await setupOverviewPage(httpSetup); }); const { component, find } = testBed; @@ -136,7 +138,7 @@ describe('Overview - Fix deprecation issues step - Elasticsearch deprecations', httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); await act(async () => { - testBed = await setupOverviewPage(); + testBed = await setupOverviewPage(httpSetup); }); const { component, find } = testBed; @@ -159,7 +161,7 @@ describe('Overview - Fix deprecation issues step - Elasticsearch deprecations', httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); await act(async () => { - testBed = await setupOverviewPage({ isReadOnlyMode: false }); + testBed = await setupOverviewPage(httpSetup, { isReadOnlyMode: false }); }); const { component, find } = testBed; @@ -182,7 +184,7 @@ describe('Overview - Fix deprecation issues step - Elasticsearch deprecations', httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); await act(async () => { - testBed = await setupOverviewPage({ isReadOnlyMode: false }); + testBed = await setupOverviewPage(httpSetup, { isReadOnlyMode: false }); }); const { component, find } = testBed; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_issues_step/fix_issues_step.test.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_issues_step/fix_issues_step.test.tsx index b7c417fbfcb8d..cc2cec97cc702 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_issues_step/fix_issues_step.test.tsx +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_issues_step/fix_issues_step.test.tsx @@ -15,10 +15,12 @@ import { esCriticalAndWarningDeprecations, esNoDeprecations } from './mock_es_is describe('Overview - Fix deprecation issues step', () => { let testBed: OverviewTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - - afterAll(() => { - server.restore(); + let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; + let httpSetup: ReturnType['httpSetup']; + beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; }); describe('when there are critical issues in one panel', () => { @@ -29,7 +31,7 @@ describe('Overview - Fix deprecation issues step', () => { const deprecationService = deprecationsServiceMock.createStartContract(); kibanaDeprecationsServiceHelpers.setLoadDeprecations({ deprecationService, response: [] }); - testBed = await setupOverviewPage({ + testBed = await setupOverviewPage(httpSetup, { services: { core: { deprecations: deprecationService, @@ -55,7 +57,7 @@ describe('Overview - Fix deprecation issues step', () => { const deprecationService = deprecationsServiceMock.createStartContract(); kibanaDeprecationsServiceHelpers.setLoadDeprecations({ deprecationService, response: [] }); - testBed = await setupOverviewPage({ + testBed = await setupOverviewPage(httpSetup, { services: { core: { deprecations: deprecationService, diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_issues_step/kibana_deprecation_issues.test.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_issues_step/kibana_deprecation_issues.test.tsx index c11a1481b68b5..f060a38440ec1 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_issues_step/kibana_deprecation_issues.test.tsx +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_issues_step/kibana_deprecation_issues.test.tsx @@ -16,12 +16,14 @@ import { esNoDeprecations } from './mock_es_issues'; describe('Overview - Fix deprecation issues step - Kibana deprecations', () => { let testBed: OverviewTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); const { mockedKibanaDeprecations, mockedCriticalKibanaDeprecations } = kibanaDeprecationsServiceHelpers.defaultMockedResponses; - - afterAll(() => { - server.restore(); + let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; + let httpSetup: ReturnType['httpSetup']; + beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; }); describe('When load succeeds', () => { @@ -33,7 +35,7 @@ describe('Overview - Fix deprecation issues step - Kibana deprecations', () => { const deprecationService = deprecationsServiceMock.createStartContract(); kibanaDeprecationsServiceHelpers.setLoadDeprecations({ deprecationService, response }); - testBed = await setupOverviewPage({ + testBed = await setupOverviewPage(httpSetup, { services: { core: { deprecations: deprecationService, @@ -114,7 +116,7 @@ describe('Overview - Fix deprecation issues step - Kibana deprecations', () => { mockRequestErrorMessage: 'Internal Server Error', }); - testBed = await setupOverviewPage({ + testBed = await setupOverviewPage(httpSetup, { services: { core: { deprecations: deprecationService, diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/migrate_system_indices/flyout.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/migrate_system_indices/flyout.test.ts index 1e74a966b3933..2d2e917555b74 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/migrate_system_indices/flyout.test.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/migrate_system_indices/flyout.test.ts @@ -13,22 +13,22 @@ import { systemIndicesMigrationStatus } from './mocks'; describe('Overview - Migrate system indices - Flyout', () => { let testBed: OverviewTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - + let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; + let httpSetup: ReturnType['httpSetup']; beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; + httpRequestsMockHelpers.setLoadSystemIndicesMigrationStatus(systemIndicesMigrationStatus); await act(async () => { - testBed = await setupOverviewPage(); + testBed = await setupOverviewPage(httpSetup); }); testBed.component.update(); }); - afterAll(() => { - server.restore(); - }); - test('shows correct features in flyout table', async () => { const { actions, table } = testBed; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/migrate_system_indices/migrate_system_indices.test.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/migrate_system_indices/migrate_system_indices.test.tsx index e3f6d747deaed..ae3e184f9c96b 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/migrate_system_indices/migrate_system_indices.test.tsx +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/migrate_system_indices/migrate_system_indices.test.tsx @@ -12,15 +12,15 @@ import { OverviewTestBed, setupOverviewPage } from '../overview.helpers'; describe('Overview - Migrate system indices', () => { let testBed: OverviewTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - + let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; + let httpSetup: ReturnType['httpSetup']; beforeEach(async () => { - testBed = await setupOverviewPage(); - testBed.component.update(); - }); + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; - afterAll(() => { - server.restore(); + testBed = await setupOverviewPage(httpSetup); + testBed.component.update(); }); describe('Error state', () => { @@ -30,7 +30,7 @@ describe('Overview - Migrate system indices', () => { message: 'error', }); - testBed = await setupOverviewPage(); + testBed = await setupOverviewPage(httpSetup); }); test('Is rendered', () => { @@ -59,7 +59,7 @@ describe('Overview - Migrate system indices', () => { migration_status: 'NO_MIGRATION_NEEDED', }); - testBed = await setupOverviewPage(); + testBed = await setupOverviewPage(httpSetup); const { exists, component } = testBed; @@ -75,7 +75,7 @@ describe('Overview - Migrate system indices', () => { migration_status: 'IN_PROGRESS', }); - testBed = await setupOverviewPage(); + testBed = await setupOverviewPage(httpSetup); const { exists, component, find } = testBed; @@ -94,7 +94,7 @@ describe('Overview - Migrate system indices', () => { migration_status: 'MIGRATION_NEEDED', }); - testBed = await setupOverviewPage(); + testBed = await setupOverviewPage(httpSetup); const { exists, component, find } = testBed; @@ -116,7 +116,7 @@ describe('Overview - Migrate system indices', () => { message: 'error', }); - testBed = await setupOverviewPage(); + testBed = await setupOverviewPage(httpSetup); const { exists, component, find } = testBed; @@ -154,7 +154,7 @@ describe('Overview - Migrate system indices', () => { ], }); - testBed = await setupOverviewPage(); + testBed = await setupOverviewPage(httpSetup); const { exists } = testBed; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/migrate_system_indices/step_completion.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/migrate_system_indices/step_completion.test.ts index 9eb0831c3c7a0..cbece74355d6d 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/migrate_system_indices/step_completion.test.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/migrate_system_indices/step_completion.test.ts @@ -13,10 +13,12 @@ import { SYSTEM_INDICES_MIGRATION_POLL_INTERVAL_MS } from '../../../../common/co describe('Overview - Migrate system indices - Step completion', () => { let testBed: OverviewTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - - afterAll(() => { - server.restore(); + let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; + let httpSetup: ReturnType['httpSetup']; + beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; }); test(`It's complete when no upgrade is needed`, async () => { @@ -25,7 +27,7 @@ describe('Overview - Migrate system indices - Step completion', () => { }); await act(async () => { - testBed = await setupOverviewPage(); + testBed = await setupOverviewPage(httpSetup); }); const { exists, component } = testBed; @@ -41,7 +43,7 @@ describe('Overview - Migrate system indices - Step completion', () => { }); await act(async () => { - testBed = await setupOverviewPage(); + testBed = await setupOverviewPage(httpSetup); }); const { exists, component } = testBed; @@ -60,7 +62,7 @@ describe('Overview - Migrate system indices - Step completion', () => { migration_status: 'IN_PROGRESS', }); - testBed = await setupOverviewPage(); + testBed = await setupOverviewPage(httpSetup); }); afterEach(() => { diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/overview.helpers.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/overview.helpers.ts index 30a531091f166..3f56a971b6bf9 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/overview.helpers.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/overview.helpers.ts @@ -7,7 +7,8 @@ import { act } from 'react-dom/test-utils'; import { registerTestBed, TestBed, AsyncTestBedConfig } from '@kbn/test-jest-helpers'; -import { Overview } from '../../../public/application/components/overview'; +import { HttpSetup } from 'src/core/public'; +import { Overview } from '../../../public/application/components'; import { WithAppDependencies } from '../helpers'; const testBedConfig: AsyncTestBedConfig = { @@ -54,9 +55,13 @@ const createActions = (testBed: TestBed) => { }; export const setupOverviewPage = async ( + httpSetup: HttpSetup, overrides?: Record ): Promise => { - const initTestBed = registerTestBed(WithAppDependencies(Overview, overrides), testBedConfig); + const initTestBed = registerTestBed( + WithAppDependencies(Overview, httpSetup, overrides), + testBedConfig + ); const testBed = await initTestBed(); return { diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/overview.test.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/overview.test.tsx index 2d318f60149d0..5e49fd510686f 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/overview.test.tsx +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/overview.test.tsx @@ -10,17 +10,11 @@ import { OverviewTestBed, setupOverviewPage } from './overview.helpers'; describe('Overview Page', () => { let testBed: OverviewTestBed; - const { server } = setupEnvironment(); - beforeEach(async () => { - testBed = await setupOverviewPage(); + testBed = await setupOverviewPage(setupEnvironment().httpSetup); testBed.component.update(); }); - afterAll(() => { - server.restore(); - }); - describe('Documentation links', () => { test('Has a whatsNew link and it references target version', () => { const { exists, find } = testBed; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/upgrade_step/upgrade_step.test.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/upgrade_step/upgrade_step.test.tsx index 9a4655d9d8ddb..dc0f7c0f7c6b0 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/upgrade_step/upgrade_step.test.tsx +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/upgrade_step/upgrade_step.test.tsx @@ -9,28 +9,33 @@ import { setupEnvironment } from '../../helpers'; import { OverviewTestBed, setupOverviewPage } from '../overview.helpers'; const DEPLOYMENT_URL = 'https://cloud.elastic.co./deployments/bfdad4ef99a24212a06d387593686d63'; -const setupCloudOverviewPage = () => { - return setupOverviewPage({ - plugins: { - cloud: { - isCloudEnabled: true, - deploymentUrl: DEPLOYMENT_URL, - }, - }, - }); -}; describe('Overview - Upgrade Step', () => { let testBed: OverviewTestBed; - const { server, httpRequestsMockHelpers, setServerAsync } = setupEnvironment(); + let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; + let httpSetup: ReturnType['httpSetup']; + let setDelayResponse: ReturnType['setDelayResponse']; + const setupCloudOverviewPage = () => { + return setupOverviewPage(httpSetup, { + plugins: { + cloud: { + isCloudEnabled: true, + deploymentUrl: DEPLOYMENT_URL, + }, + }, + }); + }; - afterAll(() => { - server.restore(); + beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; + setDelayResponse = mockEnvironment.setDelayResponse; }); describe('On-prem', () => { test('Shows link to setup upgrade docs', async () => { - testBed = await setupOverviewPage(); + testBed = await setupOverviewPage(httpSetup); const { exists } = testBed; expect(exists('upgradeSetupDocsLink')).toBe(true); @@ -75,10 +80,10 @@ describe('Overview - Upgrade Step', () => { }); test('An error callout is displayed, if status check failed', async () => { - httpRequestsMockHelpers.setGetUpgradeStatusResponse( - {}, - { statusCode: 500, message: 'Status check failed' } - ); + httpRequestsMockHelpers.setGetUpgradeStatusResponse(undefined, { + statusCode: 500, + message: 'Status check failed', + }); testBed = await setupCloudOverviewPage(); const { exists, component } = testBed; @@ -90,7 +95,7 @@ describe('Overview - Upgrade Step', () => { }); test('The CTA button displays loading indicator', async () => { - setServerAsync(true); + setDelayResponse(true); testBed = await setupCloudOverviewPage(); const { exists, find } = testBed;