From b6a059490e07677ad8ff4978a251f401deab6bc6 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 20 May 2025 17:44:41 +0100 Subject: [PATCH 01/16] [ML] Adding file upload tools to package --- .../shared/file-upload-common/index.ts | 18 +- .../file-upload-common/src/constants.ts | 12 + .../src/file_manager/auto_deploy.ts | 91 +++ .../src/file_manager/file_manager.ts | 726 ++++++++++++++++++ .../src/file_manager/file_size_check.ts | 40 + .../src/file_manager/file_wrapper.ts | 294 +++++++ .../src/file_manager/index.ts | 10 + .../src/file_manager/merge_tools.test.ts | 270 +++++++ .../src/file_manager/merge_tools.ts | 383 +++++++++ .../src/file_manager/tika_analyzer.ts | 111 +++ .../src/file_manager/tika_utils.ts | 90 +++ .../file-upload-common/src/use_file_upload.ts | 245 ++++++ .../shared/file-upload-common/src/utils.ts | 163 ++++ .../shared/file-upload-common/tsconfig.json | 5 +- .../private/file_upload/common/types.ts | 6 +- 15 files changed, 2459 insertions(+), 5 deletions(-) create mode 100644 x-pack/platform/packages/shared/file-upload-common/src/file_manager/auto_deploy.ts create mode 100644 x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_manager.ts create mode 100644 x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_size_check.ts create mode 100644 x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_wrapper.ts create mode 100644 x-pack/platform/packages/shared/file-upload-common/src/file_manager/index.ts create mode 100644 x-pack/platform/packages/shared/file-upload-common/src/file_manager/merge_tools.test.ts create mode 100644 x-pack/platform/packages/shared/file-upload-common/src/file_manager/merge_tools.ts create mode 100644 x-pack/platform/packages/shared/file-upload-common/src/file_manager/tika_analyzer.ts create mode 100644 x-pack/platform/packages/shared/file-upload-common/src/file_manager/tika_utils.ts create mode 100644 x-pack/platform/packages/shared/file-upload-common/src/use_file_upload.ts create mode 100644 x-pack/platform/packages/shared/file-upload-common/src/utils.ts diff --git a/x-pack/platform/packages/shared/file-upload-common/index.ts b/x-pack/platform/packages/shared/file-upload-common/index.ts index ce90335537fc6..c669f3b3a5c47 100644 --- a/x-pack/platform/packages/shared/file-upload-common/index.ts +++ b/x-pack/platform/packages/shared/file-upload-common/index.ts @@ -6,4 +6,20 @@ */ export type { FileUploadResults, OpenFileUploadLiteContext } from './src/types'; -export { OPEN_FILE_UPLOAD_LITE_ACTION, OPEN_FILE_UPLOAD_LITE_TRIGGER } from './src/constants'; +export { + OPEN_FILE_UPLOAD_LITE_ACTION, + OPEN_FILE_UPLOAD_LITE_TRIGGER, + FILE_FORMATS, + FILE_SIZE_DISPLAY_FORMAT, + MB, + NO_TIME_FORMAT, +} from './src/constants'; +export { + FileUploadContext, + useFileUpload, + useFileUploadContext, + UPLOAD_TYPE, +} from './src/use_file_upload'; +export { FileUploadManager } from './src/file_manager/file_manager'; +export type { UploadStatus } from './src/file_manager/file_manager'; +export type { FileAnalysis } from './src/file_manager/file_wrapper'; diff --git a/x-pack/platform/packages/shared/file-upload-common/src/constants.ts b/x-pack/platform/packages/shared/file-upload-common/src/constants.ts index e2149ea0f395f..e5fceb2a7df78 100644 --- a/x-pack/platform/packages/shared/file-upload-common/src/constants.ts +++ b/x-pack/platform/packages/shared/file-upload-common/src/constants.ts @@ -8,3 +8,15 @@ export const OPEN_FILE_UPLOAD_LITE_ACTION = 'openFileUploadLiteTrigger'; export const OPEN_FILE_UPLOAD_LITE_TRIGGER = 'OPEN_FILE_UPLOAD_LITE_TRIGGER'; + +export const FILE_FORMATS = { + DELIMITED: 'delimited', + NDJSON: 'ndjson', + SEMI_STRUCTURED_TEXT: 'semi_structured_text', + TIKA: 'tika', +}; + +export const NO_TIME_FORMAT = 'null'; +export const MB = Math.pow(2, 20); + +export const FILE_SIZE_DISPLAY_FORMAT = '0,0.[0] b'; diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/auto_deploy.ts b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/auto_deploy.ts new file mode 100644 index 0000000000000..178a2ef613ba8 --- /dev/null +++ b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/auto_deploy.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + InferenceInferenceEndpointInfo, + InferenceInferenceResponse, +} from '@elastic/elasticsearch/lib/api/types'; +import type { HttpSetup } from '@kbn/core/public'; + +const POLL_INTERVAL = 5; // seconds + +export class AutoDeploy { + private inferError: Error | null = null; + private inferFinished: boolean = false; + constructor(private readonly http: HttpSetup, private readonly inferenceId: string) {} + + public async deploy() { + this.inferError = null; + if (await this.isDeployed()) { + return; + } + + this.infer() + .then(() => { + this.inferFinished = true; + }) + .catch((e) => { + // ignore timeout errors + // The deployment may take a long time + // we'll know when it's ready from polling the inference endpoints + // looking for num_allocations + const status = e.response?.status; + if (status === 408 || status === 504 || status === 502 || status === 500) { + return; + } + this.inferError = e; + }); + await this.pollIsDeployed(); + } + + private async infer() { + return this.http.fetch( + `/internal/data_visualizer/inference/${this.inferenceId}`, + { + method: 'POST', + version: '1', + body: JSON.stringify({ input: '' }), + } + ); + } + + private async isDeployed() { + const inferenceEndpoints = await this.http.fetch( + '/internal/data_visualizer/inference_endpoints', + { + method: 'GET', + version: '1', + } + ); + + // if the call to inference has already finished, we know the model is deployed + if (this.inferFinished) { + return true; + } + + // otherwise, we need to check the allocation count + return inferenceEndpoints.some((endpoint) => { + return ( + endpoint.inference_id === this.inferenceId && endpoint.service_settings.num_allocations > 0 + ); + }); + } + + private async pollIsDeployed() { + while (true) { + if (this.inferError !== null) { + throw this.inferError; + } + const isDeployed = await this.isDeployed(); + if (isDeployed) { + // break out of the loop once we have a successful deployment + return; + } + await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL * 1000)); + } + } +} diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_manager.ts b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_manager.ts new file mode 100644 index 0000000000000..8e43321143d74 --- /dev/null +++ b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_manager.ts @@ -0,0 +1,726 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FileUploadStartApi } from '@kbn/file-upload-plugin/public/api'; + +import type { Subscription } from 'rxjs'; +import type { Observable } from 'rxjs'; +import { switchMap, combineLatest, BehaviorSubject, of } from 'rxjs'; +import type { HttpSetup } from '@kbn/core/public'; +import type { IImporter } from '@kbn/file-upload-plugin/public/importer/types'; +import type { DataViewsServicePublic } from '@kbn/data-views-plugin/public/types'; +import type { + FindFileStructureResponse, + IngestPipeline, + InitializeImportResponse, + InputOverrides, +} from '@kbn/file-upload-plugin/common/types'; +import type { + IndicesIndexSettings, + MappingTypeMapping, +} from '@elastic/elasticsearch/lib/api/types'; +import { i18n } from '@kbn/i18n'; +import type { FileUploadResults } from '../types'; +import type { FileAnalysis } from './file_wrapper'; +import { FileWrapper } from './file_wrapper'; + +import type { FileClash } from './merge_tools'; +import { + CLASH_ERROR_TYPE, + createMergedMappings, + getFormatClashes, + getMappingClashInfo, +} from './merge_tools'; +import { createUrlOverrides } from '../utils'; +import { AutoDeploy } from './auto_deploy'; + +export enum STATUS { + NA, + NOT_STARTED, + STARTED, + COMPLETED, + FAILED, +} + +export interface Config { + json: T; + string: string; + valid: boolean; + count: number; +} + +export const emptyConfig = { + json: {}, + string: '', + valid: false, +}; + +export interface UploadStatus { + analysisStatus: STATUS; + overallImportStatus: STATUS; + indexCreated: STATUS; + pipelineCreated: STATUS; + modelDeployed: STATUS; + dataViewCreated: STATUS; + pipelinesDeleted: STATUS; + fileImport: STATUS; + filesStatus: FileAnalysis[]; + fileClashes: FileClash[]; + formatMix: boolean; + mappingsJsonValid: boolean; + settingsJsonValid: boolean; + pipelinesJsonValid: boolean; + errors: Array<{ title: string; error: any }>; +} + +export class FileUploadManager { + private readonly files$ = new BehaviorSubject([]); + private readonly analysisValid$ = new BehaviorSubject(false); + public readonly fileAnalysisStatus$: Observable = this.files$.pipe( + switchMap((files) => + files.length > 0 ? combineLatest(files.map((file) => file.fileStatus$)) : of([]) + ) + ); + public readonly filePipelines$: Observable> = this.files$.pipe( + switchMap((files) => + files.length > 0 ? combineLatest(files.map((file) => file.pipelineObvs$)) : of([]) + ) + ); + private readonly existingIndexMappings$ = new BehaviorSubject(null); + + private mappingsCheckSubscription: Subscription; + public readonly settings$ = new BehaviorSubject>({ + json: {}, + string: '', + valid: false, + count: 0, + }); + public readonly mappings$ = new BehaviorSubject>({ + json: {}, + string: '', + valid: false, + count: 0, + }); + public readonly existingIndexName$ = new BehaviorSubject(null); + + private inferenceId: string | null = null; + private importer: IImporter | null = null; + private timeFieldName: string | undefined | null = null; + private commonFileFormat: string | null = null; + + public readonly uploadStatus$ = new BehaviorSubject({ + analysisStatus: STATUS.NOT_STARTED, + overallImportStatus: STATUS.NOT_STARTED, + indexCreated: STATUS.NOT_STARTED, + pipelineCreated: STATUS.NOT_STARTED, + modelDeployed: STATUS.NA, + dataViewCreated: STATUS.NOT_STARTED, + pipelinesDeleted: STATUS.NOT_STARTED, + fileImport: STATUS.NOT_STARTED, + filesStatus: [], + fileClashes: [], + formatMix: false, + mappingsJsonValid: true, + settingsJsonValid: true, + pipelinesJsonValid: true, + errors: [], + }); + private autoAddSemanticTextField: boolean = false; + + constructor( + private fileUpload: FileUploadStartApi, + private http: HttpSetup, + private dataViewsContract: DataViewsServicePublic, + private autoAddInferenceEndpointName: string | null = null, + private autoCreateDataView: boolean = true, + private removePipelinesAfterImport: boolean = true, + existingIndexName: string | null = null, + indexSettingsOverride: IndicesIndexSettings | undefined = undefined + ) { + this.setExistingIndexName(existingIndexName); + + this.autoAddSemanticTextField = this.autoAddInferenceEndpointName !== null; + this.updateSettings(indexSettingsOverride ?? {}); + + this.mappingsCheckSubscription = combineLatest([ + this.fileAnalysisStatus$, + this.existingIndexMappings$, + ]).subscribe(([statuses, existingIndexMappings]) => { + const allFilesAnalyzed = statuses.every((status) => status.loaded); + const isExistingMappingsReady = + this.getExistingIndexName() === null || existingIndexMappings !== null; + + if (allFilesAnalyzed && isExistingMappingsReady) { + this.setStatus({ + analysisStatus: STATUS.STARTED, + }); + + this.analysisValid$.next(true); + const uploadStatus = this.uploadStatus$.getValue(); + if (uploadStatus.overallImportStatus === STATUS.STARTED) { + return; + } + if (this.getFiles().length === 0) { + this.setStatus({ + fileClashes: [], + analysisStatus: STATUS.NOT_STARTED, + }); + return; + } + + const { formatsOk, fileClashes } = this.getFormatClashes(); + const { mappingClashes, mergedMappings, existingIndexChecks } = this.createMergedMappings(); + + let mappingsOk = mappingClashes.length === 0; + if (existingIndexChecks !== undefined) { + mappingsOk = mappingsOk && existingIndexChecks.mappingClashes.length === 0; + } + + const fileSizesOk = statuses.every((status) => status.fileTooLarge === false); + + if (mappingsOk && formatsOk) { + this.updateMappings(mergedMappings); + this.addSemanticTextField(); + } + + this.setStatus({ + fileClashes: + formatsOk === false + ? fileClashes + : getMappingClashInfo(mappingClashes, existingIndexChecks, statuses), + analysisStatus: mappingsOk && formatsOk && fileSizesOk ? STATUS.COMPLETED : STATUS.FAILED, + pipelinesJsonValid: this.allPipelinesValid(), + }); + } + }); + } + + destroy() { + this.files$.complete(); + this.analysisValid$.complete(); + this.settings$.complete(); + this.mappings$.complete(); + this.existingIndexMappings$.complete(); + this.uploadStatus$.complete(); + this.mappingsCheckSubscription.unsubscribe(); + } + private setStatus(status: Partial) { + this.uploadStatus$.next({ + ...this.uploadStatus$.getValue(), + ...status, + }); + } + + async addFiles(fileList: FileList) { + this.setStatus({ + analysisStatus: STATUS.STARTED, + }); + const promises = Array.from(fileList).map((file) => this.addFile(file)); + await Promise.all(promises); + } + + async addFile(file: File) { + const f = new FileWrapper(file, this.fileUpload); + const files = this.getFiles(); + files.push(f); + this.files$.next(files); + await f.analyzeFile(); + } + + async removeFile(index: number) { + const files = this.getFiles(); + const f = files[index]; + files.splice(index, 1); + this.files$.next(files); + if (f) { + f.destroy(); + } + if (files.length === 0) { + this.setStatus({ + analysisStatus: STATUS.NOT_STARTED, + }); + } + } + + public async removeClashingFiles() { + const fileClashes = this.uploadStatus$.getValue().fileClashes; + const filesToDestroy: FileWrapper[] = []; + const files = this.getFiles(); + const newFiles = files.filter((file, i) => { + if (fileClashes[i].clash === CLASH_ERROR_TYPE.ERROR) { + filesToDestroy.push(files[i]); + return false; + } + return true; + }); + + this.files$.next(newFiles); + + filesToDestroy.forEach((file) => { + file.destroy(); + }); + } + + public analyzeFileWithOverrides(index: number) { + return async (overrides: InputOverrides) => { + const files = this.getFiles(); + const file = files[index]; + if (file) { + const formattedOverrides = createUrlOverrides(overrides, {}); + await file.analyzeFile(formattedOverrides); + } + }; + } + + public getExistingIndexName() { + return this.existingIndexName$.getValue(); + } + public setExistingIndexName(name: string | null) { + this.setStatus({ + analysisStatus: STATUS.NOT_STARTED, + }); + this.existingIndexName$.next(name); + if (name === null) { + this.existingIndexMappings$.next(null); + } else { + this.loadExistingIndexMappings(); + this.autoCreateDataView = false; + } + } + + public isExistingIndexUpload() { + return this.getExistingIndexName() !== null; + } + + public getFiles() { + return this.files$.getValue(); + } + + private getFormatClashes(): { + formatsOk: boolean; + fileClashes: FileClash[]; + } { + const files = this.getFiles(); + const fileClashes = getFormatClashes(files); + const formatsOk = fileClashes.every((file) => file.clash === CLASH_ERROR_TYPE.NONE); + + if (formatsOk) { + this.commonFileFormat = formatsOk ? files[0].getStatus().results!.format : null; + } + return { + formatsOk, + fileClashes, + }; + } + + private createMergedMappings() { + const files = this.getFiles(); + return createMergedMappings( + files, + this.existingIndexMappings$.getValue() as FindFileStructureResponse['mappings'] + ); + } + + private getPipelines(): Array { + const files = this.getFiles(); + return files.map((file) => file.getPipeline()); + } + + private allPipelinesValid() { + const files = this.getFiles(); + return files.every((file) => file.isPipelineValid()); + } + + public updatePipeline(index: number) { + return (pipeline: string) => { + const files = this.getFiles(); + files[index].updatePipeline(pipeline); + }; + } + + public updatePipelines(pipelines: IngestPipeline[]) { + const files = this.getFiles(); + files.forEach((file, i) => { + file.setPipeline(pipelines[i]); + }); + } + + public getMappings() { + return this.mappings$.getValue().json; + } + + public updateMappings(mappings: MappingTypeMapping | string) { + this.updateSettingsOrMappings('mappings', mappings); + } + + public getSettings() { + return this.settings$.getValue(); + } + + public updateSettings(settings: IndicesIndexSettings | string) { + this.updateSettingsOrMappings('settings', settings); + } + + private updateSettingsOrMappings( + mode: 'settings' | 'mappings', + config: IndicesIndexSettings | MappingTypeMapping | string + ) { + const config$ = mode === 'settings' ? this.settings$ : this.mappings$; + const jsonValidKey = mode === 'settings' ? 'settingsJsonValid' : 'mappingsJsonValid'; + const currentConfig = config$.getValue(); + if (typeof config === 'string') { + try { + const json = JSON.parse(config); + const currentConfigString = JSON.stringify(currentConfig.json); + const incomingConfigString = JSON.stringify(json); + + this.setStatus({ + [jsonValidKey]: true, + }); + + if (currentConfigString === incomingConfigString) { + return; + } + + config$.next({ + json, + string: incomingConfigString, + valid: true, + count: currentConfig.count + 1, + }); + } catch (e) { + this.setStatus({ + [jsonValidKey]: false, + }); + return; + } + } else { + config$.next({ + json: config, + string: '', + valid: true, + count: currentConfig.count + 1, + }); + } + } + + public getAutoCreateDataView() { + return this.autoCreateDataView; + } + + public async import( + indexName: string, + dataViewName?: string | null + ): Promise { + const mappings = this.getMappings(); + const pipelines = this.getPipelines(); + + if (mappings === null || pipelines === null || this.commonFileFormat === null) { + this.setStatus({ + overallImportStatus: STATUS.FAILED, + }); + + return null; + } + + this.setStatus({ + overallImportStatus: STATUS.STARTED, + dataViewCreated: this.autoCreateDataView ? STATUS.NOT_STARTED : STATUS.NA, + }); + + this.importer = await this.fileUpload.importerFactory(this.commonFileFormat, {}); + this.inferenceId = getInferenceId(mappings); + + if (this.inferenceId !== null) { + this.setStatus({ + modelDeployed: STATUS.NOT_STARTED, + }); + this.setStatus({ + modelDeployed: STATUS.STARTED, + }); + await this.autoDeploy(); + this.setStatus({ + modelDeployed: STATUS.COMPLETED, + }); + } + + const createPipelines = pipelines.length > 0; + + this.setStatus({ + indexCreated: STATUS.STARTED, + pipelineCreated: createPipelines ? STATUS.STARTED : STATUS.NA, + }); + + let indexCreated = false; + let pipelinesCreated = false; + let initializeImportResp: InitializeImportResponse | undefined; + + try { + initializeImportResp = await this.importer.initializeImport( + indexName, + this.getSettings().json, + mappings, + pipelines, + this.isExistingIndexUpload() + ); + this.timeFieldName = this.importer.getTimeField(); + indexCreated = initializeImportResp.index !== undefined; + pipelinesCreated = initializeImportResp.pipelineIds.length > 0; + this.setStatus({ + indexCreated: indexCreated ? STATUS.COMPLETED : STATUS.FAILED, + ...(createPipelines + ? { pipelineCreated: pipelinesCreated ? STATUS.COMPLETED : STATUS.FAILED } + : {}), + }); + + if (initializeImportResp.error) { + throw initializeImportResp.error; + } + } catch (e) { + this.setStatus({ + overallImportStatus: STATUS.FAILED, + errors: [ + { + title: i18n.translate('xpack.dataVisualizer.file.fileManager.errorInitializing', { + defaultMessage: 'Error initializing index and ingest pipeline', + }), + error: e, + }, + ], + }); + return null; + } + + if ( + indexCreated === false || + (createPipelines && pipelinesCreated === false) || + !initializeImportResp + ) { + return null; + } + + this.setStatus({ + fileImport: STATUS.STARTED, + }); + + // import data + const files = this.getFiles(); + const createdPipelineIds = initializeImportResp.pipelineIds; + + try { + await Promise.all( + files.map(async (file, i) => { + await file.import(indexName, mappings!, createdPipelineIds[i] ?? undefined); + }) + ); + } catch (error) { + this.setStatus({ + overallImportStatus: STATUS.FAILED, + errors: [ + { + title: i18n.translate('xpack.dataVisualizer.file.fileManager.errorImportingData', { + defaultMessage: 'Error importing data', + }), + error, + }, + ], + }); + return null; + } + + this.setStatus({ + fileImport: STATUS.COMPLETED, + }); + + if (this.removePipelinesAfterImport) { + try { + this.setStatus({ + pipelinesDeleted: STATUS.STARTED, + }); + await this.importer.deletePipelines(); + this.setStatus({ + pipelinesDeleted: STATUS.COMPLETED, + }); + } catch (error) { + this.setStatus({ + pipelinesDeleted: STATUS.FAILED, + errors: [ + { + title: i18n.translate( + 'xpack.dataVisualizer.file.fileManager.errorDeletingPipelines', + { + defaultMessage: 'Error deleting pipelines', + } + ), + error, + }, + ], + }); + } + } + + let dataViewResp; + if (this.autoCreateDataView && dataViewName !== null) { + this.setStatus({ + dataViewCreated: STATUS.STARTED, + }); + const dataViewName2 = dataViewName === undefined ? indexName : dataViewName; + dataViewResp = await createKibanaDataView( + dataViewName2, + this.dataViewsContract, + this.timeFieldName ?? undefined + ); + if (dataViewResp.success === false) { + this.setStatus({ + overallImportStatus: STATUS.FAILED, + errors: [ + { + title: i18n.translate('xpack.dataVisualizer.file.fileManager.errorCreatingDataView', { + defaultMessage: 'Error creating data view', + }), + error: dataViewResp.error, + }, + ], + }); + return null; + } else { + this.setStatus({ + dataViewCreated: STATUS.COMPLETED, + }); + } + } + + this.setStatus({ + overallImportStatus: STATUS.COMPLETED, + }); + + return { + index: indexName, + dataView: dataViewResp ? { id: dataViewResp.id!, title: dataViewResp.title! } : undefined, + inferenceId: this.inferenceId ?? undefined, + timeFieldName: this.timeFieldName ?? undefined, + files: this.getFiles().map((file) => { + const status = file.getStatus(); + return { + fileName: status.fileName, + docCount: status.docCount, + fileFormat: status.results!.format, + documentType: status.results!.document_type!, + }; + }), + }; + } + + private async autoDeploy() { + if (this.inferenceId === null) { + return; + } + try { + const autoDeploy = new AutoDeploy(this.http, this.inferenceId); + await autoDeploy.deploy(); + } catch (error) { + this.setStatus({ + modelDeployed: STATUS.FAILED, + errors: [ + { + title: i18n.translate('xpack.dataVisualizer.file.fileManager.errorDeployingModel', { + defaultMessage: 'Error deploying model', + }), + error, + }, + ], + }); + } + } + + private isTikaFormat() { + return this.commonFileFormat === 'tika'; + } + + private addSemanticTextField() { + const mappings = this.getMappings(); + const pipelines = this.getPipelines(); + if ( + this.isTikaFormat() && + this.autoAddSemanticTextField && + this.autoAddInferenceEndpointName !== null && + pipelines !== null && + mappings !== null + ) { + mappings.properties!.content = { + type: 'semantic_text', + inference_id: this.autoAddInferenceEndpointName, + }; + + pipelines.forEach((pipeline) => { + if (pipeline === undefined) { + return; + } + pipeline.processors.push({ + set: { + field: 'content', + copy_from: 'attachment.content', + }, + }); + }); + } + } + + private async loadExistingIndexMappings() { + const existingIndexName = this.getExistingIndexName(); + if (existingIndexName === null) { + return; + } + try { + const { mappings } = await this.http.fetch<{ mappings: MappingTypeMapping }>( + `/api/index_management/mapping/${existingIndexName}`, + { + method: 'GET', + version: '1', + } + ); + + this.existingIndexMappings$.next(mappings); + } catch (e) { + this.existingIndexMappings$.next(null); + } + } +} + +export async function createKibanaDataView( + dataViewName: string, + dataViewsContract: DataViewsServicePublic, + timeFieldName?: string +) { + try { + const emptyPattern = await dataViewsContract.createAndSave({ + title: dataViewName, + timeFieldName, + }); + + return { + success: true, + id: emptyPattern.id, + }; + } catch (error) { + return { + success: false, + error, + id: undefined, + title: undefined, + }; + } +} + +export function getInferenceId(mappings: MappingTypeMapping) { + for (const value of Object.values(mappings.properties ?? {})) { + if (value.type === 'semantic_text' && value.inference_id) { + return value.inference_id; + } + } + return null; +} diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_size_check.ts b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_size_check.ts new file mode 100644 index 0000000000000..b968f21a03c6f --- /dev/null +++ b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_size_check.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FileUploadStartApi } from '@kbn/file-upload-plugin/public/api'; +import numeral from '@elastic/numeral'; +import { FILE_SIZE_DISPLAY_FORMAT } from '../constants'; +import { isTikaType } from './tika_utils'; + +export class FileSizeChecker { + private _maxBytes: number; + private _fileSize: number; + constructor(fileUpload: FileUploadStartApi, file: File) { + this._fileSize = file.size; + this._maxBytes = isTikaType(file.type) + ? fileUpload.getMaxTikaBytes() + : fileUpload.getMaxBytes(); + } + + public check(): boolean { + return this._fileSize <= this._maxBytes; + } + + public maxBytes(): number { + return this._maxBytes; + } + + public fileSizeFormatted(): string { + return numeral(this._fileSize).format(FILE_SIZE_DISPLAY_FORMAT); + } + public maxFileSizeFormatted(): string { + return numeral(this._maxBytes).format(FILE_SIZE_DISPLAY_FORMAT); + } + public fileSizeDiffFormatted(): string { + return numeral(this._fileSize - this._maxBytes).format(FILE_SIZE_DISPLAY_FORMAT); + } +} diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_wrapper.ts b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_wrapper.ts new file mode 100644 index 0000000000000..efeee33fa8663 --- /dev/null +++ b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_wrapper.ts @@ -0,0 +1,294 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { BehaviorSubject } from 'rxjs'; +import type { FileUploadStartApi } from '@kbn/file-upload-plugin/public/api'; +import type { + FindFileStructureResponse, + FormattedOverrides, + ImportFailure, + IngestPipeline, + InputOverrides, +} from '@kbn/file-upload-plugin/common/types'; +import type { MappingTypeMapping } from '@elastic/elasticsearch/lib/api/types'; +import { isTikaType } from './tika_utils'; +import { processResults, readFile, isSupportedFormat } from '../utils'; + +import { STATUS } from './file_manager'; +import { analyzeTikaFile } from './tika_analyzer'; +import { FileSizeChecker } from './file_size_check'; + +interface FileSizeInfo { + fileSize: number; + fileSizeFormatted: string; + maxFileSizeFormatted: string; + diffFormatted: string; +} + +interface AnalysisResults { + analysisStatus: STATUS; + fileContents: string; + results: FindFileStructureResponse | null; + explanation: string[] | undefined; + serverSettings: ReturnType | null; + overrides: FormattedOverrides; + analysisError?: any; +} + +export type FileAnalysis = AnalysisResults & { + loaded: boolean; + importStatus: STATUS; + fileName: string; + fileContents: string; + data: ArrayBuffer | null; + fileTooLarge: boolean; + fileCouldNotBeRead: boolean; + fileCouldNotBeReadPermissionError: any; + serverError: any; + results: FindFileStructureResponse | null; + explanation: string[] | undefined; + importProgress: number; + docCount: number; + supportedFormat: boolean; + failures: ImportFailure[]; + fileSizeInfo: FileSizeInfo; +}; + +export class FileWrapper { + private analyzedFile$ = new BehaviorSubject({ + analysisStatus: STATUS.NOT_STARTED, + fileContents: '', + results: null, + explanation: undefined, + serverSettings: null, + overrides: {}, + loaded: false, + importStatus: STATUS.NOT_STARTED, + fileName: '', + data: null, + fileTooLarge: false, + fileCouldNotBeRead: false, + fileCouldNotBeReadPermissionError: false, + serverError: false, + importProgress: 0, + docCount: 0, + supportedFormat: true, + failures: [], + fileSizeInfo: { + fileSize: 0, + fileSizeFormatted: '', + maxFileSizeFormatted: '', + diffFormatted: '', + }, + }); + + private pipeline$ = new BehaviorSubject(undefined); + public readonly pipelineObvs$ = this.pipeline$.asObservable(); + private pipelineJsonValid$ = new BehaviorSubject(true); + + public readonly fileStatus$ = this.analyzedFile$.asObservable(); + private fileSizeChecker: FileSizeChecker; + + constructor(private file: File, private fileUpload: FileUploadStartApi) { + this.fileSizeChecker = new FileSizeChecker(fileUpload, file); + this.analyzedFile$.next({ + ...this.analyzedFile$.getValue(), + fileName: this.file.name, + loaded: false, + fileTooLarge: !this.fileSizeChecker.check(), + fileSizeInfo: { + fileSize: this.file.size, + fileSizeFormatted: this.fileSizeChecker.fileSizeFormatted(), + maxFileSizeFormatted: this.fileSizeChecker.maxFileSizeFormatted(), + diffFormatted: this.fileSizeChecker.fileSizeDiffFormatted(), + }, + }); + } + + public destroy() { + this.analyzedFile$.complete(); + this.pipeline$.complete(); + this.pipelineJsonValid$.complete(); + } + + public async analyzeFile(overrides: InputOverrides = {}) { + this.setStatus({ analysisStatus: STATUS.STARTED }); + readFile(this.file).then(async ({ data, fileContents }) => { + // return after file has been read + // analysis will be done in the background + + let analysisResults: AnalysisResults; + let parsedFileContents = fileContents; + + if (isTikaType(this.file.type)) { + analysisResults = await this.analyzeTika(data); + parsedFileContents = analysisResults.fileContents; + } else { + analysisResults = await this.analyzeStandardFile(fileContents, overrides); + } + const supportedFormat = isSupportedFormat(analysisResults.results?.format ?? ''); + + this.setStatus({ + ...analysisResults, + loaded: true, + fileName: this.file.name, + fileContents: parsedFileContents, + data, + supportedFormat, + }); + this.setPipeline(analysisResults.results?.ingest_pipeline); + }); + } + + private async analyzeTika(data: ArrayBuffer, isRetry = false): Promise { + const { tikaResults, standardResults } = await analyzeTikaFile(data, this.fileUpload); + const serverSettings = processResults(standardResults); + + return { + fileContents: tikaResults.content, + results: standardResults.results, + explanation: standardResults.results.explanation, + serverSettings, + overrides: {}, + analysisStatus: STATUS.COMPLETED, + }; + } + + private async analyzeStandardFile( + fileContents: string, + overrides: InputOverrides, + isRetry = false + ): Promise { + try { + const resp = await this.fileUpload.analyzeFile( + fileContents, + overrides as Record + ); + + const serverSettings = processResults(resp); + + return { + fileContents, + results: resp.results, + explanation: resp.results.explanation, + serverSettings, + overrides: resp.overrides ?? {}, + analysisStatus: STATUS.COMPLETED, + }; + } catch (e) { + return { + fileContents, + results: null, + explanation: undefined, + serverSettings: null, + analysisError: e, + overrides: {}, + analysisStatus: STATUS.FAILED, + }; + } + } + + private setStatus(status: Partial) { + this.analyzedFile$.next({ + ...this.getStatus(), + ...status, + }); + } + + public getStatus() { + return this.analyzedFile$.getValue(); + } + + public getFileName() { + return this.analyzedFile$.getValue().fileName; + } + public getMappings() { + return this.analyzedFile$.getValue().results?.mappings; + } + public getPipeline(): IngestPipeline | undefined { + return this.pipeline$.getValue(); + } + public isPipelineValid() { + return this.pipelineJsonValid$.getValue(); + } + public setPipeline(pipeline: IngestPipeline | undefined) { + this.pipeline$.next(pipeline); + } + public setPipelineValid(valid: boolean) { + this.pipelineJsonValid$.next(valid); + } + public updatePipeline(pipeline: IngestPipeline | string) { + if (typeof pipeline === 'string') { + try { + const json = JSON.parse(pipeline); + const currentPipeline = this.getPipeline(); + const currentPipelineString = JSON.stringify(currentPipeline); + const incomingPipelineString = JSON.stringify(json); + + this.setPipelineValid(true); + + if (currentPipelineString === incomingPipelineString) { + return; + } + + this.setPipeline(json); + } catch (e) { + this.setPipelineValid(false); + return; + } + } else { + this.setPipeline(pipeline); + } + } + public getFormat() { + return this.analyzedFile$.getValue().results?.format; + } + public getData() { + return this.analyzedFile$.getValue().data; + } + + public getFileSizeInfo() { + return { + fileSizeFormatted: this.fileSizeChecker.fileSizeFormatted(), + maxFileSizeFormatted: this.fileSizeChecker.maxFileSizeFormatted(), + diffFormatted: this.fileSizeChecker.fileSizeDiffFormatted(), + }; + } + + public async import(index: string, mappings: MappingTypeMapping, pipelineId: string | undefined) { + this.setStatus({ importStatus: STATUS.STARTED }); + const format = this.analyzedFile$.getValue().results!.format; + const importer = await this.fileUpload.importerFactory(format, { + excludeLinesPattern: this.analyzedFile$.getValue().results!.exclude_lines_pattern, + multilineStartPattern: this.analyzedFile$.getValue().results!.multiline_start_pattern, + }); + + const ingestPipeline = this.getPipeline(); + importer.initializeWithoutCreate(index, mappings, ingestPipeline ? [ingestPipeline] : []); + const data = this.getData(); + if (data === null) { + this.setStatus({ importStatus: STATUS.FAILED }); + return; + } + importer.read(data); + try { + const resp = await importer.import(index, pipelineId, (p) => { + this.setStatus({ importProgress: p }); + }); + + this.setStatus({ + docCount: resp.docCount, + failures: resp.failures ?? [], + importStatus: STATUS.COMPLETED, + }); + return resp; + } catch (error) { + this.setStatus({ importStatus: STATUS.FAILED }); + return; + } + } +} diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/index.ts b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/index.ts new file mode 100644 index 0000000000000..f43165562fefa --- /dev/null +++ b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { FileUploadManager, STATUS, type Config, type UploadStatus } from './file_manager'; +export { FileWrapper, type FileAnalysis } from './file_wrapper'; +export { CLASH_ERROR_TYPE, CLASH_TYPE, type FileClash, type MappingClash } from './merge_tools'; diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/merge_tools.test.ts b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/merge_tools.test.ts new file mode 100644 index 0000000000000..2eb75f5b70f1a --- /dev/null +++ b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/merge_tools.test.ts @@ -0,0 +1,270 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + createMergedMappings, + getMappingClashInfo, + getFormatClashes, + CLASH_TYPE, + CLASH_ERROR_TYPE, +} from './merge_tools'; +import type { FileWrapper, FileAnalysis } from './file_wrapper'; + +describe('merge_tools', () => { + describe('createMergedMappings', () => { + it('should return merged mappings when all mappings are the same', () => { + const files = [ + { + getMappings: () => ({ properties: { field1: { type: 'text' } } }), + } as never as FileWrapper, + { + getMappings: () => ({ properties: { field1: { type: 'text' } } }), + } as never as FileWrapper, + ]; + + const result = createMergedMappings(files, null); + expect(result.mergedMappings).toEqual({ properties: { field1: { type: 'text' } } }); + expect(result.mappingClashes).toEqual([]); + }); + + it('should return merged mappings when a keyword mapping is being merged with a text mapping', () => { + const files = [ + { + getMappings: () => ({ properties: { field1: { type: 'text' } } }), + } as never as FileWrapper, + { + getMappings: () => ({ properties: { field1: { type: 'keyword' } } }), + } as never as FileWrapper, + ]; + + const result = createMergedMappings(files, null); + expect(result.mergedMappings).toEqual({ properties: { field1: { type: 'text' } } }); + expect(result.mappingClashes).toEqual([]); + }); + + it('should return merged mappings when a text mapping is being merged with a keyword mapping', () => { + const files = [ + { + getMappings: () => ({ properties: { field1: { type: 'keyword' } } }), + } as never as FileWrapper, + { + getMappings: () => ({ properties: { field1: { type: 'text' } } }), + } as never as FileWrapper, + ]; + + const result = createMergedMappings(files, null); + expect(result.mergedMappings).toEqual({ properties: { field1: { type: 'text' } } }); + expect(result.mappingClashes).toEqual([]); + }); + + it('should return mapping clashes when mappings are different', () => { + const files = [ + { + getFileName: () => 'file1', + getMappings: () => ({ properties: { field1: { type: 'text' } } }), + } as never as FileWrapper, + { + getFileName: () => 'file2', + getMappings: () => ({ properties: { field1: { type: 'number' } } }), + } as never as FileWrapper, + ]; + + const result = createMergedMappings(files, null); + expect(result.mergedMappings).toEqual({ properties: { field1: { type: 'text' } } }); + expect(result.mappingClashes).toEqual([ + { + fieldName: 'field1', + existingType: 'text', + clashingType: { fileName: 'file2', newType: 'number', fileIndex: 1 }, + }, + ]); + }); + + it('should return mapping clashes when first field doesn`t match the others', () => { + const files = [ + { + getFileName: () => 'file1', + getMappings: () => ({ properties: { field1: { type: 'text' } } }), + } as never as FileWrapper, + { + getFileName: () => 'file2', + getMappings: () => ({ properties: { field1: { type: 'number' } } }), + } as never as FileWrapper, + { + getFileName: () => 'file3', + getMappings: () => ({ properties: { field1: { type: 'number' } } }), + } as never as FileWrapper, + ]; + + const result = createMergedMappings(files, null); + + expect(result.mergedMappings).toEqual({ properties: { field1: { type: 'text' } } }); + expect(result.mappingClashes).toEqual([ + { + fieldName: 'field1', + existingType: 'text', + clashingType: { + fileName: 'file2', + newType: 'number', + fileIndex: 1, + }, + }, + { + fieldName: 'field1', + existingType: 'text', + clashingType: { + fileName: 'file3', + newType: 'number', + fileIndex: 2, + }, + }, + ]); + }); + }); + + describe('getMappingClashInfo', () => { + it('should return file clashes based on mapping clashes', () => { + const mappingClashes = [ + { + fieldName: 'field1', + existingType: 'text', + clashingType: { + fileName: 'file2', + newType: 'number', + fileIndex: 1, + }, + }, + { + fieldName: 'field1', + existingType: 'text', + clashingType: { + fileName: 'file3', + newType: 'number', + fileIndex: 2, + }, + }, + ]; + + const filesStatus = [ + { fileName: 'file1', supportedFormat: true } as FileAnalysis, + { fileName: 'file2', supportedFormat: true } as FileAnalysis, + { fileName: 'file3', supportedFormat: true } as FileAnalysis, + ]; + + const result = getMappingClashInfo(mappingClashes, undefined, filesStatus); + + expect(result).toEqual([ + { fileName: 'file1', clash: CLASH_ERROR_TYPE.ERROR, clashType: CLASH_TYPE.MAPPING }, + { fileName: 'file2', clash: CLASH_ERROR_TYPE.NONE, clashType: CLASH_TYPE.MAPPING }, + { fileName: 'file3', clash: CLASH_ERROR_TYPE.NONE, clashType: CLASH_TYPE.MAPPING }, + ]); + }); + }); + + describe('getFormatClashes', () => { + it('should return no clashes when all formats are supported', () => { + const files = [ + { + getFormat: () => 'semi_structured_text', + getFileName: () => 'file1', + getStatus: () => ({ supportedFormat: true }), + } as FileWrapper, + { + getFormat: () => 'semi_structured_text', + getFileName: () => 'file2', + getStatus: () => ({ supportedFormat: true }), + } as FileWrapper, + ]; + + const result = getFormatClashes(files); + expect(result).toEqual([ + { fileName: 'file1', clash: CLASH_ERROR_TYPE.NONE }, + { fileName: 'file2', clash: CLASH_ERROR_TYPE.NONE }, + ]); + }); + + it('should return format clashes when formats are different', () => { + const files = [ + { + getFormat: () => 'semi_structured_text', + getFileName: () => 'file1', + getStatus: () => ({ supportedFormat: true }), + } as FileWrapper, + { + getFormat: () => 'delimited', + getFileName: () => 'file2', + getStatus: () => ({ supportedFormat: true }), + } as FileWrapper, + ]; + + const result = getFormatClashes(files); + expect(result).toEqual([ + { fileName: 'file1', clash: CLASH_ERROR_TYPE.ERROR, clashType: CLASH_TYPE.FORMAT }, + { fileName: 'file2', clash: CLASH_ERROR_TYPE.ERROR, clashType: CLASH_TYPE.FORMAT }, + ]); + }); + + it('should choose the correct file that clashes', () => { + const files = [ + { + getFormat: () => 'semi_structured_text', + getFileName: () => 'file1', + getStatus: () => ({ supportedFormat: true }), + } as FileWrapper, + { + getFormat: () => 'semi_structured_text', + getFileName: () => 'file2', + getStatus: () => ({ supportedFormat: true }), + } as FileWrapper, + { + getFormat: () => 'delimited', + getFileName: () => 'file3', + getStatus: () => ({ supportedFormat: true }), + } as FileWrapper, + ]; + + const result = getFormatClashes(files); + + expect(result).toEqual([ + { fileName: 'file1', clash: CLASH_ERROR_TYPE.NONE, clashType: undefined }, + { fileName: 'file2', clash: CLASH_ERROR_TYPE.NONE, clashType: undefined }, + { fileName: 'file3', clash: CLASH_ERROR_TYPE.ERROR, clashType: 1 }, + ]); + }); + + it('should return unsupported format clashes', () => { + const files = [ + { + getFormat: () => 'semi_structured_text', + getFileName: () => 'file1', + getStatus: () => ({ supportedFormat: true }), + getMappings: () => ({ properties: { field1: { type: 'text' } } }), + } as unknown as FileWrapper, + { + getFormat: () => 'semi_structured_text', + getFileName: () => 'file2', + getStatus: () => ({ supportedFormat: true }), + getMappings: () => ({ properties: { field1: { type: 'text' } } }), + } as unknown as FileWrapper, + { + getFormat: () => 'unknown', + getFileName: () => 'file3', + getStatus: () => ({ supportedFormat: false }), + getMappings: () => ({ properties: { field1: { type: 'text' } } }), + } as unknown as FileWrapper, + ]; + + const result = getFormatClashes(files); + + expect(result).toEqual([ + { fileName: 'file1', clash: CLASH_ERROR_TYPE.NONE, clashType: undefined }, + { fileName: 'file2', clash: CLASH_ERROR_TYPE.NONE, clashType: undefined }, + { fileName: 'file3', clash: CLASH_ERROR_TYPE.ERROR, clashType: CLASH_TYPE.UNSUPPORTED }, + ]); + }); + }); +}); diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/merge_tools.ts b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/merge_tools.ts new file mode 100644 index 0000000000000..7412978f26f28 --- /dev/null +++ b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/merge_tools.ts @@ -0,0 +1,383 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FindFileStructureResponse } from '@kbn/file-upload-plugin/common/types'; +import type { MappingPropertyBase, MappingTypeMapping } from '@elastic/elasticsearch/lib/api/types'; +import type { FileAnalysis, FileWrapper } from './file_wrapper'; + +export enum CLASH_TYPE { + MAPPING, + FORMAT, + UNSUPPORTED, + EXISTING_INDEX_MAPPING, + MANY_NEW_FIELDS, + MANY_UNUSED_FIELDS, +} + +export enum CLASH_ERROR_TYPE { + NONE, + ERROR, + WARNING, +} + +// export const NEW_FIELD_THRESHOLD = 10; +// export const UNUSED_FIELD_THRESHOLD = 10; + +export interface MappingClash { + fieldName: string; + existingType: string; + clashingType: { fileName: string; newType: string; fileIndex: number }; +} + +export interface FileClash { + fileName: string; + clash: CLASH_ERROR_TYPE; + clashType?: CLASH_TYPE; + newFields?: string[]; + missingFields?: string[]; + commonFields?: string[]; +} + +interface MergedMappings { + mergedMappings: MappingTypeMapping; + mappingClashes: MappingClash[]; + existingIndexChecks?: ExistingIndexChecks; +} + +interface FieldsPerFile { + fileName: string; + fileIndex: number; + fields: string[]; +} + +interface ExistingIndexChecks { + existingFields: string[]; + newFieldsPerFile: FieldsPerFile[]; + mappingClashes: MappingClash[]; + unmappedFieldsPerFile: FieldsPerFile[]; + commonFieldsPerFile: FieldsPerFile[]; +} + +export function createMergedMappings( + files: FileWrapper[], + existingIndexMappings: FindFileStructureResponse['mappings'] | null +): MergedMappings { + const checkExistingIndexMappings = existingIndexMappings !== null; + + const mappings = files.map((file) => file.getMappings() ?? { properties: {} }); + + // stringify each mappings and see if they are the same, if so return the first one. + // otherwise drill down and extract each field with it's type. + const mappingsString = mappings.map((m) => JSON.stringify(m)); + const mappingComparator = checkExistingIndexMappings + ? JSON.stringify(existingIndexMappings) + : mappingsString[0]; + + if (mappingsString.every((m) => m === mappingComparator)) { + return { mergedMappings: mappings[0] as MappingTypeMapping, mappingClashes: [] }; + } + + const fieldsPerFile = mappings.map((m) => getFieldsFromMappings(m as MappingTypeMapping)); + + if (existingIndexMappings !== null) { + // add the existing index mappings to the beginning of the fields array + // so the merged mappings contain the existing index mappings + fieldsPerFile.splice(0, 0, getFieldsFromMappings(existingIndexMappings as MappingTypeMapping)); + } + + const mappingClashes: MappingClash[] = []; + + const mergedMappingsMap = fieldsPerFile.reduce((acc, fields, i) => { + fields.forEach((field) => { + if (!acc.has(field.name)) { + acc.set(field.name, field.value); + } else { + const existingField = acc.get(field.name); + + if (existingField && existingField.type !== field.value.type) { + // if either new or existing field is text or keyword, we should allow the clash + // and replace the existing field with the new field if the existing is keyword = + if (existingField.type === 'keyword' && field.value.type === 'text') { + // the existing field is keyword and the new field is text, replace the existing field with the text version + acc.set(field.name, field.value); + } else if (existingField.type === 'text' && field.value.type === 'keyword') { + // do nothing + } else { + mappingClashes.push({ + fieldName: field.name, + existingType: existingField.type, + clashingType: { + fileName: files[i].getFileName(), + newType: field.value.type as string, + fileIndex: i, + }, + }); + } + } + } + }); + return acc; + }, new Map()); + + if (existingIndexMappings !== null) { + // remove the existing index mappings from the fields array + // so the indices of fieldsPerFile match the files array + fieldsPerFile.splice(0, 1); + } + + const mergedMappings = { + properties: Object.fromEntries(mergedMappingsMap), + } as MappingTypeMapping; + + if (checkExistingIndexMappings === true) { + const existingIndexChecks: ExistingIndexChecks = { + existingFields: [], + newFieldsPerFile: [], + mappingClashes: [], + unmappedFieldsPerFile: [], + commonFieldsPerFile: [], + }; + + const existingFields = getFieldsFromMappings(existingIndexMappings as MappingTypeMapping); + const existingFieldMap = existingFields.reduce((acc, field) => { + acc.set(field.name, field.value); + return acc; + }, new Map()); + + existingIndexChecks.existingFields = existingFields.map((field) => field.name); + + for (const [i, fields] of fieldsPerFile.entries()) { + const newFieldsPerFile: FieldsPerFile = { + fileName: files[i].getFileName(), + fileIndex: i, + fields: [], + }; + const commonFieldsPerFile: FieldsPerFile = { + fileName: files[i].getFileName(), + fileIndex: i, + fields: [], + }; + + for (const field of fields) { + const existingField = existingFieldMap.get(field.name); + if (existingField !== undefined) { + commonFieldsPerFile.fields.push(field.name); + + const existingType = existingField.type; + if (existingType !== field.value.type) { + if (existingType === 'text' && field.value.type === 'keyword') { + // do nothing + } else { + existingIndexChecks.mappingClashes.push({ + fieldName: field.name, + existingType, + clashingType: { + fileName: files[i].getFileName(), + newType: field.value.type as string, + fileIndex: i, + }, + }); + } + } + } else { + newFieldsPerFile.fields.push(field.name); + } + } + + existingIndexChecks.newFieldsPerFile.push(newFieldsPerFile); + existingIndexChecks.commonFieldsPerFile.push(commonFieldsPerFile); + existingIndexChecks.unmappedFieldsPerFile.push({ + fileName: files[i].getFileName(), + fileIndex: i, + fields: existingIndexChecks.existingFields.filter( + (field) => !commonFieldsPerFile.fields.includes(field) + ), + }); + } + + return { mergedMappings, mappingClashes, existingIndexChecks }; + } + + return { mergedMappings, mappingClashes }; +} + +export function getMappingClashInfo( + mappingClashes: MappingClash[], + existingIndexChecks: ExistingIndexChecks | undefined, + filesStatus: FileAnalysis[] +): FileClash[] { + const clashCounts: Array<{ index: number; count: number }> = filesStatus + .map((file, i) => { + const counts = { index: i, count: 0 }; + mappingClashes.forEach((clash) => { + if (clash.clashingType.fileIndex === i) { + counts.count++; + } + }); + return counts; + }) + .sort((a, b) => b.count - a.count); + + const middleIndex = Math.floor(clashCounts.length / 2); + + const median = clashCounts[middleIndex].count; + + const medianAboveZero = median > 0; + const zeroCount = clashCounts.filter((c) => c.count === 0).length; + const aboveZeroCount = clashCounts.length - zeroCount; + const allClash = zeroCount === aboveZeroCount; + + clashCounts.sort((a, b) => a.index - b.index); + + const existingIndexClashes = (existingIndexChecks?.mappingClashes ?? []).reduce((acc, clash) => { + acc.set(clash.clashingType.fileIndex, true); + return acc; + }, new Map()); + + return clashCounts.map((c, i) => { + const fileName = filesStatus[c.index].fileName; + let fileClash: FileClash; + if (existingIndexClashes.has(c.index)) { + fileClash = { + fileName, + clash: CLASH_ERROR_TYPE.ERROR, + clashType: CLASH_TYPE.EXISTING_INDEX_MAPPING, + }; + } else { + fileClash = { + fileName, + clash: + allClash || + (medianAboveZero === false && c.count > 0) || + (medianAboveZero && c.count === 0) + ? CLASH_ERROR_TYPE.ERROR + : CLASH_ERROR_TYPE.NONE, + clashType: CLASH_TYPE.MAPPING, + }; + } + if (existingIndexChecks?.newFieldsPerFile) { + const newFields = existingIndexChecks.newFieldsPerFile[i]; + if (newFields) { + fileClash.newFields = newFields.fields; + } + } + if (existingIndexChecks?.unmappedFieldsPerFile) { + const unmappedFieldsPerFile = existingIndexChecks.unmappedFieldsPerFile; + fileClash.missingFields = unmappedFieldsPerFile[i].fields; + } + if (existingIndexChecks?.commonFieldsPerFile) { + const commonFields = existingIndexChecks.commonFieldsPerFile[i]; + if (commonFields) { + fileClash.commonFields = commonFields.fields; + } + } + if (fileClash.clash !== CLASH_ERROR_TYPE.ERROR) { + // if the file contains many new fields but none of them are in the existing index + // set the clash to warning + if ( + fileClash.missingFields && + existingIndexChecks?.existingFields && + existingIndexChecks?.existingFields.length > 0 && + fileClash.missingFields.length > (existingIndexChecks.existingFields.length - 1) / 2 + ) { + fileClash.clash = CLASH_ERROR_TYPE.WARNING; + } + } + return fileClash; + }); +} + +export function getFormatClashes(files: FileWrapper[]): FileClash[] { + const formatMap = files + .map((file) => file.getFormat()) + .reduce((acc, format, i) => { + if (format === undefined) { + acc.set('unknown', (acc.get('unknown') ?? 0) + 1); + } else { + acc.set(format, (acc.get(format) ?? 0) + 1); + } + return acc; + }, new Map()); + + // return early if there is only one format and it is supported + if (formatMap.size === 1 && formatMap.has('unknown') === false) { + return files.map((f) => ({ + fileName: f.getFileName(), + clash: CLASH_ERROR_TYPE.NONE, + })); + } + + const formatArray = Array.from(formatMap.entries()).sort((a, b) => b[1] - a[1]); + + const fileClashes = files.map((f) => { + return { + fileName: f.getFileName(), + clash: + f.getStatus().supportedFormat === false ? CLASH_ERROR_TYPE.ERROR : CLASH_ERROR_TYPE.NONE, + clashType: CLASH_TYPE.UNSUPPORTED, + }; + }); + + const topCount = formatArray[0]; + + // if the top count is for an unsupported format, + // return the fileClashes containing unsupported formats + if (topCount[0] === 'unknown') { + return fileClashes; + } + + // Check if all counts are the same, + // mark all files as clashing + const counts = Array.from(formatMap.values()); + const allCountsSame = counts.every((count) => count === counts[0]); + if (allCountsSame) { + return files.map((f) => { + return { + fileName: f.getFileName(), + clash: CLASH_ERROR_TYPE.ERROR, + clashType: f.getStatus().supportedFormat ? CLASH_TYPE.FORMAT : CLASH_TYPE.UNSUPPORTED, + }; + }); + } + + return files.map((f) => { + let clashType: CLASH_TYPE | undefined; + if (f.getStatus().supportedFormat === false) { + clashType = CLASH_TYPE.UNSUPPORTED; + } else if (f.getFormat() !== topCount[0]) { + clashType = CLASH_TYPE.FORMAT; + } + + return { + fileName: f.getFileName(), + clash: clashType !== undefined ? CLASH_ERROR_TYPE.ERROR : CLASH_ERROR_TYPE.NONE, + clashType, + }; + }); +} + +export function getFieldsFromMappings(mappings: MappingTypeMapping) { + const fields: Array<{ name: string; value: { type: string } }> = []; + + function traverseProperties(properties: MappingPropertyBase, parentKey: string = '') { + for (const [key, value] of Object.entries(properties)) { + const fullKey = parentKey ? `${parentKey}.${key}` : key; + + if (value.properties) { + traverseProperties(value.properties, fullKey); + } else if (value.type) { + fields.push({ name: fullKey, value }); + } + } + } + + if (mappings.properties) { + traverseProperties(mappings.properties); + } + + return fields.sort((a, b) => a.name.localeCompare(b.name)); +} diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/tika_analyzer.ts b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/tika_analyzer.ts new file mode 100644 index 0000000000000..b876aeb62e2ef --- /dev/null +++ b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/tika_analyzer.ts @@ -0,0 +1,111 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AnalysisResult, PreviewTikaResponse } from '@kbn/file-upload-plugin/common/types'; +import type { FileUploadStartApi } from '@kbn/file-upload-plugin/public/api'; +import { FILE_FORMATS } from '../constants'; + +export async function analyzeTikaFile( + data: ArrayBuffer, + fileUpload: FileUploadStartApi +): Promise<{ standardResults: AnalysisResult; tikaResults: PreviewTikaResponse }> { + const resp = await fileUpload.previewTikaFile(data); + const numLinesAnalyzed = (resp.content.match(/\n/g) || '').length + 1; + + return { + tikaResults: resp, + standardResults: { + results: { + format: FILE_FORMATS.TIKA, + document_type: resp.content_type, + charset: 'utf-8', + has_header_row: false, + has_byte_order_marker: false, + sample_start: '', + quote: '', + delimiter: '', + need_client_timezone: false, + num_lines_analyzed: numLinesAnalyzed, + num_messages_analyzed: 0, + explanation: [], + field_stats: { + // @ts-expect-error semantic_text not supported + 'attachment.content': {}, + // @ts-expect-error semantic_text not supported + 'attachment.content_length': {}, + // @ts-expect-error semantic_text not supported + 'attachment.content_type': {}, + // @ts-expect-error semantic_text not supported + 'attachment.format': {}, + // @ts-expect-error semantic_text not supported + 'attachment.language': {}, + }, + mappings: { + properties: { + attachment: { + // @ts-expect-error semantic_text not supported + properties: { + content: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + content_length: { + type: 'long', + }, + content_type: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + format: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + language: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + }, + }, + }, + }, + ingest_pipeline: { + description: 'Ingest pipeline created by file data visualizer', + processors: [ + { + attachment: { + field: 'data', + remove_binary: true, + // unlimited character count + indexed_chars: -1, + }, + }, + ], + }, + }, + }, + }; +} diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/tika_utils.ts b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/tika_utils.ts new file mode 100644 index 0000000000000..934378464d70a --- /dev/null +++ b/x-pack/platform/packages/shared/file-upload-common/src/file_manager/tika_utils.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export function isTikaType(type: string) { + return getTikaDisplayType(type).isTikaType; +} + +export const getTikaDisplayType = (type: string): { isTikaType: boolean; label: string } => { + switch (type) { + case 'application/doc': + case 'application/ms-doc': + case 'application/msword': + case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': + return { + isTikaType: true, + label: i18n.translate('xpack.dataVisualizer.file.tikaTypes.word', { + defaultMessage: 'Microsoft Office Word document', + }), + }; + + case 'application/excel': + case 'application/vnd.ms-excel': + case 'application/x-excel': + case 'application/x-msexcel': + case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': + return { + isTikaType: true, + label: i18n.translate('xpack.dataVisualizer.file.tikaTypes.excel', { + defaultMessage: 'Microsoft Office Excel document', + }), + }; + + case 'application/mspowerpoint': + case 'application/powerpoint': + case 'application/vnd.ms-powerpoint': + case 'application/x-mspowerpoint': + case 'application/vnd.openxmlformats-officedocument.presentationml.presentation': + return { + isTikaType: true, + label: i18n.translate('xpack.dataVisualizer.file.tikaTypes.powerPoint', { + defaultMessage: 'Microsoft Office Power Point document', + }), + }; + + case 'application/vnd.oasis.opendocument.presentation': + case 'application/vnd.oasis.opendocument.spreadsheet': + case 'application/vnd.oasis.opendocument.text': + return { + isTikaType: true, + label: i18n.translate('xpack.dataVisualizer.file.tikaTypes.openDoc', { + defaultMessage: 'Open Document Format', + }), + }; + + case 'text/rtf': + case 'application/rtf': + return { + isTikaType: true, + label: i18n.translate('xpack.dataVisualizer.file.tikaTypes.richText', { + defaultMessage: 'Rich Text Format', + }), + }; + + case 'application/pdf': + return { + isTikaType: true, + label: i18n.translate('xpack.dataVisualizer.file.tikaTypes.pdf', { + defaultMessage: 'PDF', + }), + }; + + case 'text/plain': + case 'text/plain; charset=UTF-8': + return { + isTikaType: true, + label: i18n.translate('xpack.dataVisualizer.file.tikaTypes.plainText', { + defaultMessage: 'Plain text', + }), + }; + + default: + return { isTikaType: false, label: type }; + } +}; diff --git a/x-pack/platform/packages/shared/file-upload-common/src/use_file_upload.ts b/x-pack/platform/packages/shared/file-upload-common/src/use_file_upload.ts new file mode 100644 index 0000000000000..5e058a3639a66 --- /dev/null +++ b/x-pack/platform/packages/shared/file-upload-common/src/use_file_upload.ts @@ -0,0 +1,245 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import useObservable from 'react-use/lib/useObservable'; +import { i18n } from '@kbn/i18n'; +import type { Index } from '@kbn/index-management-shared-types/src/types'; +import type { ApplicationStart, HttpSetup } from '@kbn/core/public'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { FileUploadResults } from '..'; +import { FileUploadManager, STATUS } from './file_manager/file_manager'; +import { CLASH_ERROR_TYPE } from './file_manager/merge_tools'; + +export enum UPLOAD_TYPE { + NEW = 'new', + EXISTING = 'existing', +} + +export function useFileUpload( + fileUploadManager: FileUploadManager, + data: DataPublicPluginStart, + application: ApplicationStart, + onUploadComplete?: (results: FileUploadResults | null) => void, + http?: HttpSetup +) { + const { dataViews } = data; + const { navigateToApp } = application; + + const [importResults, setImportResults] = useState(null); + const [indexCreateMode, setIndexCreateMode] = useState(UPLOAD_TYPE.NEW); + const [indices, setIndices] = useState([]); + + const [indexName, setIndexName] = useState( + fileUploadManager.getExistingIndexName() ?? '' + ); + const [dataViewName, setDataViewName] = useState( + fileUploadManager.getAutoCreateDataView() ? '' : null + ); + + const [existingDataViewNames, setExistingDataViewNames] = useState([]); + + const [dataViewNameError, setDataViewNameError] = useState(''); + + const [indexValidationStatus, setIndexValidationStatus] = useState( + fileUploadManager.getExistingIndexName() ? STATUS.COMPLETED : STATUS.NOT_STARTED + ); + + const deleteFile = useCallback( + (i: number) => fileUploadManager.removeFile(i), + [fileUploadManager] + ); + + const filesStatus = useObservable(fileUploadManager.fileAnalysisStatus$, []); + const uploadStatus = useObservable( + fileUploadManager.uploadStatus$, + fileUploadManager.uploadStatus$.getValue() + ); + const fileClashes = useMemo( + () => uploadStatus.fileClashes.some((f) => f.clash === CLASH_ERROR_TYPE.ERROR), + [uploadStatus.fileClashes] + ); + + const fullFileUpload = useCallback( + () => navigateToApp('home', { path: '#/tutorial_directory/fileDataViz' }), + [navigateToApp] + ); + + useEffect(() => { + if (http === undefined) { + return; + } + + http.get('/api/index_management/indices').then((indx) => { + setIndices(indx.filter((i) => i.hidden === false && i.isFrozen === false)); + }); + }, [http, fileUploadManager]); + + useEffect(() => { + dataViews.getTitles().then((titles) => { + setExistingDataViewNames(titles); + }); + }, [dataViews]); + + useEffect(() => { + return () => { + fileUploadManager.destroy(); + }; + }, [fileUploadManager]); + + useEffect(() => { + if (dataViewName === null || dataViewName === '') { + setDataViewNameError(''); + return; + } + + setDataViewNameError(isDataViewNameValid(dataViewName, existingDataViewNames, indexName)); + }, [dataViewName, existingDataViewNames, indexName]); + + const uploadInProgress = + uploadStatus.overallImportStatus === STATUS.STARTED || + uploadStatus.overallImportStatus === STATUS.COMPLETED || + uploadStatus.overallImportStatus === STATUS.FAILED; + + const onImportClick = useCallback(() => { + const existingIndex = fileUploadManager.getExistingIndexName(); + const index = existingIndex !== null ? existingIndex : indexName; + const dv = dataViewName === '' ? undefined : dataViewName; + fileUploadManager.import(index, dv).then((res) => { + if (onUploadComplete && res) { + onUploadComplete(res); + } + setImportResults(res); + }); + }, [dataViewName, fileUploadManager, indexName, onUploadComplete]); + + const existingIndexName = useObservable( + fileUploadManager.existingIndexName$, + fileUploadManager.getExistingIndexName() + ); + + const canImport = useMemo(() => { + return ( + uploadStatus.analysisStatus === STATUS.COMPLETED && + indexValidationStatus === STATUS.COMPLETED && + ((existingIndexName === null && indexName !== '') || existingIndexName !== null) && + uploadStatus.mappingsJsonValid === true && + uploadStatus.settingsJsonValid === true && + uploadStatus.pipelinesJsonValid === true && + dataViewNameError === '' + ); + }, [ + dataViewNameError, + indexName, + indexValidationStatus, + uploadStatus.analysisStatus, + uploadStatus.mappingsJsonValid, + uploadStatus.pipelinesJsonValid, + uploadStatus.settingsJsonValid, + existingIndexName, + ]); + + const mappings = useObservable( + fileUploadManager.mappings$, + fileUploadManager.mappings$.getValue() + ); + const settings = useObservable( + fileUploadManager.settings$, + fileUploadManager.settings$.getValue() + ); + + const setExistingIndexName = useCallback( + (idxName: string | null) => { + fileUploadManager.setExistingIndexName(idxName); + }, + [fileUploadManager] + ); + + useEffect(() => { + setIndexName(''); + setExistingIndexName(null); + }, [indexCreateMode, setExistingIndexName]); + + const pipelines = useObservable(fileUploadManager.filePipelines$, []); + + return { + fileUploadManager, + indexName, + setIndexName, + indexValidationStatus, + setIndexValidationStatus, + deleteFile, + filesStatus, + uploadStatus, + fileClashes, + fullFileUpload, + uploadInProgress, + onImportClick, + canImport, + mappings, + settings, + pipelines, + importResults, + dataViewName, + setDataViewName, + dataViewNameError, + indexCreateMode, + setIndexCreateMode, + indices, + existingIndexName, + setExistingIndexName, + }; +} + +function isDataViewNameValid(name: string, dataViewNames: string[], index: string) { + // if a blank name is entered, the index name will be used so avoid validation + if (name === '') { + return ''; + } + + if (dataViewNames.find((i) => i === name)) { + return i18n.translate( + 'xpack.dataVisualizer.file.importView.dataViewNameAlreadyExistsErrorMessage', + { + defaultMessage: 'Data view name already exists', + } + ); + } + + // escape . and + to stop the regex matching more than it should. + let newName = name.replace(/\./g, '\\.'); + newName = newName.replace(/\+/g, '\\+'); + // replace * with .* to make the wildcard match work. + newName = newName.replace(/\*/g, '.*'); + const reg = new RegExp(`^${newName}$`); + if (index.match(reg) === null) { + // name should match index + return i18n.translate( + 'xpack.dataVisualizer.file.importView.indexPatternDoesNotMatchDataViewErrorMessage', + { + defaultMessage: 'Data view does not match index name', + } + ); + } + + return ''; +} + +type FileUploadContextValue = ReturnType; + +export const FileUploadContext = createContext(undefined); + +export const useFileUploadContext = (): FileUploadContextValue => { + const fileUploadContext = useContext(FileUploadContext); + + // if `undefined`, throw an error + if (fileUploadContext === undefined) { + throw new Error('useFileUploadContext was used outside of its Provider'); + } + + return fileUploadContext; +}; diff --git a/x-pack/platform/packages/shared/file-upload-common/src/utils.ts b/x-pack/platform/packages/shared/file-upload-common/src/utils.ts new file mode 100644 index 0000000000000..d740dbb71e60a --- /dev/null +++ b/x-pack/platform/packages/shared/file-upload-common/src/utils.ts @@ -0,0 +1,163 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isEqual } from 'lodash'; +import type { AnalysisResult, InputOverrides } from '@kbn/file-upload-plugin/common'; +import type { FormattedOverrides } from '@kbn/file-upload-plugin/common/types'; +import { MB, FILE_FORMATS, NO_TIME_FORMAT } from './constants'; + +export const DEFAULT_LINES_TO_SAMPLE = 1000; +const UPLOAD_SIZE_MB = 5; + +const overrideDefaults = { + timestampFormat: undefined, + timestampField: undefined, + format: undefined, + delimiter: undefined, + quote: undefined, + hasHeaderRow: undefined, + charset: undefined, + columnNames: undefined, + shouldTrimFields: undefined, + grokPattern: undefined, + linesToSample: undefined, +}; + +export function readFile(file: File): Promise<{ fileContents: string; data: ArrayBuffer }> { + return new Promise((resolve, reject) => { + if (file && file.size) { + const reader = new FileReader(); + reader.readAsArrayBuffer(file); + + reader.onload = (() => { + return () => { + const decoder = new TextDecoder(); + const data = reader.result; + if (data === null || typeof data === 'string') { + return reject(); + } + const size = UPLOAD_SIZE_MB * MB; + const fileContents = decoder.decode(data.slice(0, size)); + + if (fileContents === '') { + reject(); + } else { + resolve({ fileContents, data }); + } + }; + })(); + } else { + reject(); + } + }); +} + +export function createUrlOverrides(overrides: InputOverrides, originalSettings: InputOverrides) { + const formattedOverrides: FormattedOverrides = {}; + for (const o in overrideDefaults) { + if (Object.hasOwn(overrideDefaults, o)) { + let value = overrides[o]; + if ( + (Array.isArray(value) && isEqual(value, originalSettings[o])) || + value === undefined || + value === originalSettings[o] + ) { + value = ''; + } + + const snakeCaseO = o.replace(/([A-Z])/g, ($1) => `_${$1.toLowerCase()}`); + formattedOverrides[snakeCaseO] = value; + } + } + + if (formattedOverrides.format === '' && originalSettings.format === FILE_FORMATS.DELIMITED) { + if ( + formattedOverrides.should_trim_fields !== '' || + formattedOverrides.has_header_row !== '' || + formattedOverrides.delimiter !== '' || + formattedOverrides.quote !== '' || + formattedOverrides.column_names !== '' + ) { + formattedOverrides.format = originalSettings.format; + } + } + + if ( + formattedOverrides.format === FILE_FORMATS.DELIMITED && + Array.isArray(formattedOverrides.column_names) + ) { + formattedOverrides.column_names = formattedOverrides.column_names.join(); + } + + if ( + formattedOverrides.format === '' && + originalSettings.format === FILE_FORMATS.SEMI_STRUCTURED_TEXT + ) { + if (formattedOverrides.grok_pattern !== '') { + formattedOverrides.format = originalSettings.format; + } + } + + if ( + formattedOverrides.format === FILE_FORMATS.NDJSON || + originalSettings.format === FILE_FORMATS.NDJSON + ) { + formattedOverrides.should_trim_fields = ''; + formattedOverrides.has_header_row = ''; + formattedOverrides.delimiter = ''; + formattedOverrides.quote = ''; + formattedOverrides.column_names = ''; + } + + if (formattedOverrides.lines_to_sample === '') { + formattedOverrides.lines_to_sample = overrides.linesToSample; + } + + return formattedOverrides; +} + +export function processResults({ results, overrides }: AnalysisResult) { + let timestampFormat; + if ( + (overrides && overrides.timestamp_format === NO_TIME_FORMAT) || + results.java_timestamp_formats === undefined + ) { + timestampFormat = NO_TIME_FORMAT; + } else if (results.java_timestamp_formats.length) { + timestampFormat = results.java_timestamp_formats[0]; + } + + const linesToSample = + overrides !== undefined && overrides.lines_to_sample !== undefined + ? overrides.lines_to_sample + : DEFAULT_LINES_TO_SAMPLE; + + return { + format: results.format, + delimiter: results.delimiter, + timestampField: results.timestamp_field, + timestampFormat, + quote: results.quote, + hasHeaderRow: results.has_header_row, + shouldTrimFields: results.should_trim_fields, + charset: results.charset, + columnNames: results.column_names, + grokPattern: results.grok_pattern, + linesToSample, + }; +} + +export type ServerSettings = ReturnType | null; + +export function isSupportedFormat(format: string) { + return ( + format === FILE_FORMATS.NDJSON || + format === FILE_FORMATS.DELIMITED || + format === FILE_FORMATS.SEMI_STRUCTURED_TEXT || + format === FILE_FORMATS.TIKA + ); +} diff --git a/x-pack/platform/packages/shared/file-upload-common/tsconfig.json b/x-pack/platform/packages/shared/file-upload-common/tsconfig.json index 7aba1b1a9378a..54c9fae971e7b 100644 --- a/x-pack/platform/packages/shared/file-upload-common/tsconfig.json +++ b/x-pack/platform/packages/shared/file-upload-common/tsconfig.json @@ -13,5 +13,8 @@ "exclude": [ "target/**/*" ], - "kbn_references": [] + "kbn_references": [ + "@kbn/file-upload-plugin", + "@kbn/index-management-shared-types", + ] } diff --git a/x-pack/platform/plugins/private/file_upload/common/types.ts b/x-pack/platform/plugins/private/file_upload/common/types.ts index 33ac6993541f7..ff56fe7e0496c 100644 --- a/x-pack/platform/plugins/private/file_upload/common/types.ts +++ b/x-pack/platform/plugins/private/file_upload/common/types.ts @@ -13,9 +13,9 @@ export interface InputOverrides { } export type FormattedOverrides = InputOverrides & { - column_names: string[]; - has_header_row: boolean; - should_trim_fields: boolean; + column_names?: string[] | string; + has_header_row?: boolean | string; + should_trim_fields?: boolean | string; }; export interface AnalysisResult { From ec04ec63c4033d1fa7365a5e2c4cb37032dfe6cc Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 20 May 2025 16:55:12 +0000 Subject: [PATCH 02/16] [CI] Auto-commit changed files from 'node scripts/notice' --- .../platform/packages/shared/file-upload-common/tsconfig.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/platform/packages/shared/file-upload-common/tsconfig.json b/x-pack/platform/packages/shared/file-upload-common/tsconfig.json index 54c9fae971e7b..f508b4a96f82e 100644 --- a/x-pack/platform/packages/shared/file-upload-common/tsconfig.json +++ b/x-pack/platform/packages/shared/file-upload-common/tsconfig.json @@ -16,5 +16,9 @@ "kbn_references": [ "@kbn/file-upload-plugin", "@kbn/index-management-shared-types", + "@kbn/core", + "@kbn/data-views-plugin", + "@kbn/i18n", + "@kbn/data-plugin", ] } From ae3567ad8677a92d41f00cbb43dac5d923f331c7 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 21 May 2025 10:51:20 +0100 Subject: [PATCH 03/16] adding server side changes --- .../shared/file-upload-common/src/types.ts | 1 + .../private/file_upload/common/constants.ts | 2 ++ .../file_upload/public/importer/importer.ts | 4 +++- .../file_upload/public/importer/routes.ts | 3 +++ .../file_upload/public/importer/types.ts | 3 ++- .../private/file_upload/server/import_data.ts | 17 +++++++++++++++-- .../private/file_upload/server/routes.ts | 10 ++++++++-- .../private/file_upload/server/schemas.ts | 1 + 8 files changed, 35 insertions(+), 6 deletions(-) diff --git a/x-pack/platform/packages/shared/file-upload-common/src/types.ts b/x-pack/platform/packages/shared/file-upload-common/src/types.ts index f4223d4660d94..59522d8c476a9 100644 --- a/x-pack/platform/packages/shared/file-upload-common/src/types.ts +++ b/x-pack/platform/packages/shared/file-upload-common/src/types.ts @@ -13,6 +13,7 @@ export interface FileUploadResults { dataView?: { id: string; title: string }; inferenceId?: string; files: Array<{ fileName: string; docCount: number; fileFormat: string; documentType: string }>; + timeFieldName?: string; } export interface OpenFileUploadLiteContext { diff --git a/x-pack/platform/plugins/private/file_upload/common/constants.ts b/x-pack/platform/plugins/private/file_upload/common/constants.ts index 991dccac56f04..2ccf1315618c0 100644 --- a/x-pack/platform/plugins/private/file_upload/common/constants.ts +++ b/x-pack/platform/plugins/private/file_upload/common/constants.ts @@ -25,3 +25,5 @@ export const FILE_FORMATS = { }; export const TIKA_PREVIEW_CHARS = 100000; + +// move all of these to package!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/x-pack/platform/plugins/private/file_upload/public/importer/importer.ts b/x-pack/platform/plugins/private/file_upload/public/importer/importer.ts index 6f09f35e57752..19009d1814b50 100644 --- a/x-pack/platform/plugins/private/file_upload/public/importer/importer.ts +++ b/x-pack/platform/plugins/private/file_upload/public/importer/importer.ts @@ -123,7 +123,8 @@ export abstract class Importer implements IImporter { index: string, settings: IndicesIndexSettings, mappings: MappingTypeMapping, - pipelines: Array + pipelines: Array, + existingIndex: boolean = false ) { this._initialize(index, mappings, pipelines); @@ -132,6 +133,7 @@ export abstract class Importer implements IImporter { settings, mappings, ingestPipelines: this._pipelines, + existingIndex, }); } diff --git a/x-pack/platform/plugins/private/file_upload/public/importer/routes.ts b/x-pack/platform/plugins/private/file_upload/public/importer/routes.ts index 5987344168906..d0ab6c9db8443 100644 --- a/x-pack/platform/plugins/private/file_upload/public/importer/routes.ts +++ b/x-pack/platform/plugins/private/file_upload/public/importer/routes.ts @@ -22,6 +22,7 @@ interface CallInitializeImportRoute { settings: IndicesIndexSettings; mappings: MappingTypeMapping; ingestPipelines?: IngestPipelineWrapper[]; + existingIndex?: boolean; } interface CallImportRoute { @@ -35,12 +36,14 @@ export function callInitializeImportRoute({ settings, mappings, ingestPipelines, + existingIndex, }: CallInitializeImportRoute) { const body = JSON.stringify({ index, settings, mappings, ingestPipelines, + existingIndex, }); return getHttp().fetch({ diff --git a/x-pack/platform/plugins/private/file_upload/public/importer/types.ts b/x-pack/platform/plugins/private/file_upload/public/importer/types.ts index dca09e3b45a9f..2b031a2feecc0 100644 --- a/x-pack/platform/plugins/private/file_upload/public/importer/types.ts +++ b/x-pack/platform/plugins/private/file_upload/public/importer/types.ts @@ -49,7 +49,8 @@ export interface IImporter { index: string, settings: IndicesIndexSettings, mappings: MappingTypeMapping, - pipeline: Array + pipeline: Array, + existingIndex?: boolean ): Promise; initializeWithoutCreate( index: string, diff --git a/x-pack/platform/plugins/private/file_upload/server/import_data.ts b/x-pack/platform/plugins/private/file_upload/server/import_data.ts index 67ee822f07d80..5ade5bf3e3489 100644 --- a/x-pack/platform/plugins/private/file_upload/server/import_data.ts +++ b/x-pack/platform/plugins/private/file_upload/server/import_data.ts @@ -26,13 +26,18 @@ export function importDataProvider({ asCurrentUser }: IScopedClusterClient) { index: string, settings: IndicesIndexSettings, mappings: MappingTypeMapping, - ingestPipelines: IngestPipelineWrapper[] + ingestPipelines: IngestPipelineWrapper[], + existingIndex: boolean = false ): Promise { let createdIndex; const createdPipelineIds: Array = []; const id = generateId(); try { - await createIndex(index, settings, mappings); + if (existingIndex) { + await updateMappings(index, mappings); + } else { + await createIndex(index, settings, mappings); + } createdIndex = index; // create the pipeline if one has been supplied @@ -132,6 +137,14 @@ export function importDataProvider({ asCurrentUser }: IScopedClusterClient) { await asCurrentUser.indices.create({ index, ...body }, { maxRetries: 0 }); } + async function updateMappings(index: string, mappings: MappingTypeMapping) { + const resp = await asCurrentUser.indices.getMapping({ index }); + const existingMappings = resp[index]?.mappings; + if (JSON.stringify(existingMappings.properties) !== JSON.stringify(mappings.properties)) { + await asCurrentUser.indices.putMapping({ index, ...mappings }); + } + } + async function indexData(index: string, pipelineId: string | undefined, data: InputData) { try { const body = []; diff --git a/x-pack/platform/plugins/private/file_upload/server/routes.ts b/x-pack/platform/plugins/private/file_upload/server/routes.ts index 5a3f400f1a73f..8199adb8d1f43 100644 --- a/x-pack/platform/plugins/private/file_upload/server/routes.ts +++ b/x-pack/platform/plugins/private/file_upload/server/routes.ts @@ -162,13 +162,19 @@ export function fileUploadRoutes(coreSetup: CoreSetup, logge }, async (context, request, response) => { try { - const { index, settings, mappings, ingestPipelines } = request.body; + const { index, settings, mappings, ingestPipelines, existingIndex } = request.body; const esClient = (await context.core).elasticsearch.client; await updateTelemetry(); const { initializeImport } = importDataProvider(esClient); - const result = await initializeImport(index, settings, mappings, ingestPipelines); + const result = await initializeImport( + index, + settings, + mappings, + ingestPipelines, + existingIndex + ); return response.ok({ body: result }); } catch (e) { diff --git a/x-pack/platform/plugins/private/file_upload/server/schemas.ts b/x-pack/platform/plugins/private/file_upload/server/schemas.ts index 76da1622862ea..54551bdcf4e0f 100644 --- a/x-pack/platform/plugins/private/file_upload/server/schemas.ts +++ b/x-pack/platform/plugins/private/file_upload/server/schemas.ts @@ -39,6 +39,7 @@ export const initializeImportFileBodySchema = schema.object({ mappings: schema.any(), /** Ingest pipeline definition */ ingestPipelines: schema.arrayOf(ingestPipeline), + existingIndex: schema.maybe(schema.boolean()), }); export const importFileBodySchema = schema.object({ From 846c0a86d4fd14f284c8df6d6dbc2f1fb8742b97 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 21 May 2025 14:07:26 +0100 Subject: [PATCH 04/16] fixing lite flyout --- .../shared/file-upload-common/src/types.ts | 1 + .../public/lite/file_clash_warning.tsx | 5 +- .../public/lite/file_manager/file_manager.ts | 480 ------------------ .../public/lite/file_manager/file_wrapper.ts | 211 -------- .../public/lite/file_manager/index.ts | 8 - .../lite/file_manager/merge_tools.test.ts | 269 ---------- .../public/lite/file_manager/merge_tools.ts | 196 ------- .../public/lite/file_picker.tsx | 64 +-- .../public/lite/file_status.tsx | 7 +- .../public/lite/file_upload_lite.tsx | 100 ++-- .../public/lite/file_upload_lite_action.tsx | 4 +- .../public/lite/file_upload_lite_view.tsx | 127 ++--- .../public/lite/flyout/component_wrapper.tsx | 75 --- .../public/lite/flyout/create_flyout.tsx | 43 +- .../public/lite/flyout/flyout.tsx | 46 -- .../public/lite/flyout/flyout_contents.tsx | 55 -- .../public/lite/import_errors.tsx | 2 +- .../public/lite/index_input.tsx | 10 +- .../public/lite/overall_upload_progress.tsx | 2 +- .../public/lite/overall_upload_status.tsx | 5 +- 20 files changed, 156 insertions(+), 1554 deletions(-) delete mode 100644 x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/file_manager.ts delete mode 100644 x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/file_wrapper.ts delete mode 100644 x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/index.ts delete mode 100644 x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/merge_tools.test.ts delete mode 100644 x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/merge_tools.ts delete mode 100644 x-pack/platform/plugins/private/data_visualizer/public/lite/flyout/component_wrapper.tsx delete mode 100644 x-pack/platform/plugins/private/data_visualizer/public/lite/flyout/flyout.tsx delete mode 100644 x-pack/platform/plugins/private/data_visualizer/public/lite/flyout/flyout_contents.tsx diff --git a/x-pack/platform/packages/shared/file-upload-common/src/types.ts b/x-pack/platform/packages/shared/file-upload-common/src/types.ts index 59522d8c476a9..d04d3b35481c5 100644 --- a/x-pack/platform/packages/shared/file-upload-common/src/types.ts +++ b/x-pack/platform/packages/shared/file-upload-common/src/types.ts @@ -21,6 +21,7 @@ export interface OpenFileUploadLiteContext { indexSettings?: IndicesIndexSettings; autoAddInference?: string; autoCreateDataView?: boolean; + existingIndex?: string; initialIndexName?: string; flyoutContent?: FlyoutContent; } diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_clash_warning.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_clash_warning.tsx index a1b99815718f9..9db916a06e5d5 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_clash_warning.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_clash_warning.tsx @@ -9,9 +9,8 @@ import React from 'react'; import { EuiButton, EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { FileAnalysis } from './file_manager/file_wrapper'; -import type { UploadStatus } from './file_manager/file_manager'; -import { CLASH_TYPE } from './file_manager/merge_tools'; +import type { UploadStatus, FileAnalysis } from '@kbn/file-upload-common'; +import { CLASH_TYPE } from '@kbn/file-upload-common/src/file_manager'; interface Props { uploadStatus: UploadStatus; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/file_manager.ts b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/file_manager.ts deleted file mode 100644 index 2110504a61ce1..0000000000000 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/file_manager.ts +++ /dev/null @@ -1,480 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { FileUploadStartApi } from '@kbn/file-upload-plugin/public/api'; - -import type { Subscription } from 'rxjs'; -import type { Observable } from 'rxjs'; -import { switchMap, combineLatest, BehaviorSubject, of } from 'rxjs'; -import type { HttpSetup } from '@kbn/core/public'; -import type { IImporter } from '@kbn/file-upload-plugin/public/importer/types'; -import type { DataViewsServicePublic } from '@kbn/data-views-plugin/public/types'; -import type { - IngestPipeline, - InitializeImportResponse, -} from '@kbn/file-upload-plugin/common/types'; -import type { - IndicesIndexSettings, - MappingTypeMapping, -} from '@elastic/elasticsearch/lib/api/types'; -import { i18n } from '@kbn/i18n'; -import type { FileUploadResults } from '@kbn/file-upload-common'; -import type { FileAnalysis } from './file_wrapper'; -import { FileWrapper } from './file_wrapper'; -import { - createKibanaDataView, - getInferenceId, -} from '../../application/file_data_visualizer/components/import_view/import'; -import { AutoDeploy } from '../../application/file_data_visualizer/components/import_view/auto_deploy'; -import type { FileClash } from './merge_tools'; -import { createMergedMappings, getFormatClashes, getMappingClashInfo } from './merge_tools'; - -export enum STATUS { - NA, - NOT_STARTED, - STARTED, - COMPLETED, - FAILED, -} - -export interface UploadStatus { - analysisOk: boolean; - overallImportStatus: STATUS; - indexCreated: STATUS; - pipelineCreated: STATUS; - modelDeployed: STATUS; - dataViewCreated: STATUS; - pipelinesDeleted: STATUS; - fileImport: STATUS; - filesStatus: FileAnalysis[]; - fileClashes: FileClash[]; - formatMix: boolean; - errors: Array<{ title: string; error: any }>; -} - -export class FileManager { - private readonly files$ = new BehaviorSubject([]); - private readonly analysisValid$ = new BehaviorSubject(false); - public readonly fileAnalysisStatus$: Observable = this.files$.pipe( - switchMap((files) => - files.length > 0 ? combineLatest(files.map((file) => file.fileStatus$)) : of([]) - ) - ); - private mappingsCheckSubscription: Subscription; - private settings; - private mappings: MappingTypeMapping | null = null; - private pipelines: Array = []; - private inferenceId: string | null = null; - private importer: IImporter | null = null; - private timeFieldName: string | undefined | null = null; - private commonFileFormat: string | null = null; - - public readonly uploadStatus$ = new BehaviorSubject({ - analysisOk: false, - overallImportStatus: STATUS.NOT_STARTED, - indexCreated: STATUS.NOT_STARTED, - pipelineCreated: STATUS.NOT_STARTED, - modelDeployed: STATUS.NA, - dataViewCreated: STATUS.NOT_STARTED, - pipelinesDeleted: STATUS.NOT_STARTED, - fileImport: STATUS.NOT_STARTED, - filesStatus: [], - fileClashes: [], - formatMix: false, - errors: [], - }); - private autoAddSemanticTextField: boolean = false; - - constructor( - private fileUpload: FileUploadStartApi, - private http: HttpSetup, - private dataViewsContract: DataViewsServicePublic, - private autoAddInferenceEndpointName: string | null = null, - private autoCreateDataView: boolean = true, - private removePipelinesAfterImport: boolean = true, - indexSettingsOverride: IndicesIndexSettings | undefined = undefined - ) { - this.autoAddSemanticTextField = this.autoAddInferenceEndpointName !== null; - this.settings = indexSettingsOverride ?? {}; - - this.mappingsCheckSubscription = this.fileAnalysisStatus$.subscribe((statuses) => { - const allFilesAnalyzed = statuses.every((status) => status.loaded); - if (allFilesAnalyzed) { - this.analysisValid$.next(true); - const uploadStatus = this.uploadStatus$.getValue(); - if (uploadStatus.fileImport === STATUS.STARTED) { - return; - } - if (this.getFiles().length === 0) { - this.setStatus({ - fileClashes: [], - }); - return; - } - - const { formatsOk, fileClashes } = this.getFormatClashes(); - const { mappingClashes, mergedMappings } = this.createMergedMappings(); - const mappingsOk = mappingClashes.length === 0; - if (formatsOk === false) { - this.setStatus({ - fileClashes, - }); - } else if (mappingsOk) { - this.mappings = mergedMappings; - this.pipelines = this.getPipelines(); - this.addSemanticTextField(); - this.setStatus({ - fileClashes: [], - }); - } else { - this.setStatus({ - fileClashes: getMappingClashInfo(mappingClashes, statuses), - }); - } - this.setStatus({ - analysisOk: mappingsOk && formatsOk, - }); - } - }); - } - - destroy() { - this.files$.complete(); - this.mappingsCheckSubscription.unsubscribe(); - } - private setStatus(status: Partial) { - this.uploadStatus$.next({ - ...this.uploadStatus$.getValue(), - ...status, - }); - } - - async addFiles(fileList: FileList) { - this.setStatus({ - analysisOk: false, - }); - const promises = Array.from(fileList).map((file) => this.addFile(file)); - await Promise.all(promises); - } - - async addFile(file: File) { - const f = new FileWrapper(file, this.fileUpload); - const files = this.getFiles(); - files.push(f); - this.files$.next(files); - await f.analyzeFile(); - } - - async removeFile(index: number) { - const files = this.getFiles(); - const f = files[index]; - files.splice(index, 1); - this.files$.next(files); - if (f) { - f.destroy(); - } - } - - public async removeClashingFiles() { - const fileClashes = this.uploadStatus$.getValue().fileClashes; - const filesToDestroy: FileWrapper[] = []; - const files = this.getFiles(); - const newFiles = files.filter((file, i) => { - if (fileClashes[i].clash) { - filesToDestroy.push(files[i]); - return false; - } - return true; - }); - - this.files$.next(newFiles); - - filesToDestroy.forEach((file) => { - file.destroy(); - }); - } - - public getFiles() { - return this.files$.getValue(); - } - - private getFormatClashes(): { - formatsOk: boolean; - fileClashes: FileClash[]; - } { - const files = this.getFiles(); - const fileClashes = getFormatClashes(files); - const formatsOk = fileClashes.every((file) => file.clash === false); - - if (formatsOk) { - this.commonFileFormat = formatsOk ? files[0].getStatus().results!.format : null; - } - return { - formatsOk, - fileClashes, - }; - } - - private createMergedMappings() { - const files = this.getFiles(); - return createMergedMappings(files); - } - - private getPipelines(): Array { - const files = this.getFiles(); - return files.map((file) => file.getPipeline()); - } - - public async import(indexName: string): Promise { - if (this.mappings === null || this.pipelines === null || this.commonFileFormat === null) { - this.setStatus({ - overallImportStatus: STATUS.FAILED, - }); - - return null; - } - - this.setStatus({ - overallImportStatus: STATUS.STARTED, - dataViewCreated: this.autoCreateDataView ? STATUS.NOT_STARTED : STATUS.NA, - }); - - this.importer = await this.fileUpload.importerFactory(this.commonFileFormat, {}); - this.inferenceId = getInferenceId(this.mappings); - - if (this.inferenceId !== null) { - this.setStatus({ - modelDeployed: STATUS.NOT_STARTED, - }); - this.setStatus({ - modelDeployed: STATUS.STARTED, - }); - await this.autoDeploy(); - this.setStatus({ - modelDeployed: STATUS.COMPLETED, - }); - } - - const createPipelines = this.pipelines.length > 0; - - this.setStatus({ - indexCreated: STATUS.STARTED, - pipelineCreated: createPipelines ? STATUS.STARTED : STATUS.NA, - }); - - let indexCreated = false; - let pipelinesCreated = false; - let initializeImportResp: InitializeImportResponse | undefined; - - try { - initializeImportResp = await this.importer.initializeImport( - indexName, - this.settings, - this.mappings, - this.pipelines - ); - this.timeFieldName = this.importer.getTimeField(); - indexCreated = initializeImportResp.index !== undefined; - pipelinesCreated = initializeImportResp.pipelineIds.length > 0; - this.setStatus({ - indexCreated: indexCreated ? STATUS.COMPLETED : STATUS.FAILED, - ...(createPipelines - ? { pipelineCreated: pipelinesCreated ? STATUS.COMPLETED : STATUS.FAILED } - : {}), - }); - - if (initializeImportResp.error) { - throw initializeImportResp.error; - } - } catch (e) { - this.setStatus({ - overallImportStatus: STATUS.FAILED, - errors: [ - { - title: i18n.translate('xpack.dataVisualizer.file.fileManager.errorInitializing', { - defaultMessage: 'Error initializing index and ingest pipeline', - }), - error: e, - }, - ], - }); - return null; - } - - if ( - indexCreated === false || - (createPipelines && pipelinesCreated === false) || - !initializeImportResp - ) { - return null; - } - - this.setStatus({ - fileImport: STATUS.STARTED, - }); - - // import data - const files = this.getFiles(); - const createdPipelineIds = initializeImportResp.pipelineIds; - - try { - await Promise.all( - files.map(async (file, i) => { - await file.import(indexName, this.mappings!, createdPipelineIds[i] ?? undefined); - }) - ); - } catch (error) { - this.setStatus({ - overallImportStatus: STATUS.FAILED, - errors: [ - { - title: i18n.translate('xpack.dataVisualizer.file.fileManager.errorImportingData', { - defaultMessage: 'Error importing data', - }), - error, - }, - ], - }); - return null; - } - - this.setStatus({ - fileImport: STATUS.COMPLETED, - }); - - if (this.removePipelinesAfterImport) { - try { - this.setStatus({ - pipelinesDeleted: STATUS.STARTED, - }); - await this.importer.deletePipelines(); - this.setStatus({ - pipelinesDeleted: STATUS.COMPLETED, - }); - } catch (error) { - this.setStatus({ - pipelinesDeleted: STATUS.FAILED, - errors: [ - { - title: i18n.translate( - 'xpack.dataVisualizer.file.fileManager.errorDeletingPipelines', - { - defaultMessage: 'Error deleting pipelines', - } - ), - error, - }, - ], - }); - } - } - - const dataView = ''; - let dataViewResp; - if (this.autoCreateDataView) { - this.setStatus({ - dataViewCreated: STATUS.STARTED, - }); - const dataViewName = dataView === '' ? indexName : dataView; - dataViewResp = await createKibanaDataView( - dataViewName, - this.dataViewsContract, - this.timeFieldName ?? undefined - ); - if (dataViewResp.success === false) { - this.setStatus({ - overallImportStatus: STATUS.FAILED, - errors: [ - { - title: i18n.translate('xpack.dataVisualizer.file.fileManager.errorCreatingDataView', { - defaultMessage: 'Error creating data view', - }), - error: dataViewResp.error, - }, - ], - }); - return null; - } else { - this.setStatus({ - dataViewCreated: STATUS.COMPLETED, - }); - } - } - - this.setStatus({ - overallImportStatus: STATUS.COMPLETED, - }); - - return { - index: indexName, - dataView: dataViewResp ? { id: dataViewResp.id!, title: dataView! } : undefined, - inferenceId: this.inferenceId ?? undefined, - files: this.getFiles().map((file) => { - const status = file.getStatus(); - return { - fileName: status.fileName, - docCount: status.docCount, - fileFormat: status.results!.format, - documentType: status.results!.document_type!, - }; - }), - }; - } - - private async autoDeploy() { - if (this.inferenceId === null) { - return; - } - try { - const autoDeploy = new AutoDeploy(this.http, this.inferenceId); - await autoDeploy.deploy(); - } catch (error) { - this.setStatus({ - modelDeployed: STATUS.FAILED, - errors: [ - { - title: i18n.translate('xpack.dataVisualizer.file.fileManager.errorDeployingModel', { - defaultMessage: 'Error deploying model', - }), - error, - }, - ], - }); - } - } - - private isTikaFormat() { - return this.commonFileFormat === 'tika'; - } - - private addSemanticTextField() { - if ( - this.isTikaFormat() && - this.autoAddSemanticTextField && - this.autoAddInferenceEndpointName !== null && - this.pipelines !== null && - this.mappings !== null - ) { - this.mappings.properties!.content = { - type: 'semantic_text', - inference_id: this.autoAddInferenceEndpointName, - }; - - this.pipelines.forEach((pipeline) => { - if (pipeline === undefined) { - return; - } - pipeline.processors.push({ - set: { - field: 'content', - copy_from: 'attachment.content', - }, - }); - }); - } - } -} diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/file_wrapper.ts b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/file_wrapper.ts deleted file mode 100644 index 94e300cb475d3..0000000000000 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/file_wrapper.ts +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { BehaviorSubject } from 'rxjs'; -import type { FileUploadStartApi } from '@kbn/file-upload-plugin/public/api'; -import type { - FindFileStructureResponse, - IngestPipeline, -} from '@kbn/file-upload-plugin/common/types'; -import type { MappingTypeMapping } from '@elastic/elasticsearch/lib/api/types'; -import { isSupportedFormat } from '../../../common/constants'; -import { isTikaType } from '../../../common/utils/tika_utils'; -import { processResults, readFile } from '../../application/common/components/utils'; -import { analyzeTikaFile } from '../../application/file_data_visualizer/components/file_data_visualizer_view/tika_analyzer'; -import { STATUS } from './file_manager'; -import { FileSizeChecker } from '../../application/file_data_visualizer/components/file_data_visualizer_view/file_size_check'; - -interface AnalysisResults { - analysisStatus: STATUS; - fileContents: string; - results: FindFileStructureResponse | null; - explanation: string[] | undefined; - serverSettings: ReturnType | null; - analysisError?: any; -} - -export type FileAnalysis = AnalysisResults & { - loaded: boolean; - importStatus: STATUS; - fileName: string; - fileContents: string; - fileSize: string; - data: ArrayBuffer | null; - fileTooLarge: boolean; - fileCouldNotBeRead: boolean; - fileCouldNotBeReadPermissionError: any; - serverError: any; - results: FindFileStructureResponse | null; - explanation: string[] | undefined; - importProgress: number; - docCount: number; - supportedFormat: boolean; -}; - -export class FileWrapper { - private analyzedFile$ = new BehaviorSubject({ - analysisStatus: STATUS.NOT_STARTED, - fileContents: '', - fileSize: '', - results: null, - explanation: undefined, - serverSettings: null, - loaded: false, - importStatus: STATUS.NOT_STARTED, - fileName: '', - data: null, - fileTooLarge: false, - fileCouldNotBeRead: false, - fileCouldNotBeReadPermissionError: false, - serverError: false, - importProgress: 0, - docCount: 0, - supportedFormat: true, - }); - - public readonly fileStatus$ = this.analyzedFile$.asObservable(); - private fileSizeChecker: FileSizeChecker; - - constructor(private file: File, private fileUpload: FileUploadStartApi) { - this.fileSizeChecker = new FileSizeChecker(fileUpload, file); - this.analyzedFile$.next({ - ...this.analyzedFile$.getValue(), - fileName: this.file.name, - loaded: false, - fileTooLarge: !this.fileSizeChecker.check(), - fileSize: this.fileSizeChecker.fileSizeFormatted(), - }); - } - - public destroy() { - this.analyzedFile$.complete(); - } - - public async analyzeFile() { - this.setStatus({ analysisStatus: STATUS.STARTED }); - readFile(this.file).then(async ({ data, fileContents }) => { - // return after file has been read - // analysis will be done in the background - - let analysisResults: AnalysisResults; - let parsedFileContents = fileContents; - - if (isTikaType(this.file.type)) { - analysisResults = await this.analyzeTika(data); - parsedFileContents = analysisResults.fileContents; - } else { - analysisResults = await this.analyzeStandardFile(fileContents, {}); - } - const supportedFormat = isSupportedFormat(analysisResults.results?.format ?? ''); - - this.setStatus({ - ...analysisResults, - loaded: true, - fileName: this.file.name, - fileContents: parsedFileContents, - data, - supportedFormat, - }); - }); - } - - private async analyzeTika(data: ArrayBuffer, isRetry = false): Promise { - const { tikaResults, standardResults } = await analyzeTikaFile(data, this.fileUpload); - const serverSettings = processResults(standardResults); - - return { - fileContents: tikaResults.content, - results: standardResults.results, - explanation: standardResults.results.explanation, - serverSettings, - analysisStatus: STATUS.COMPLETED, - }; - } - - private async analyzeStandardFile( - fileContents: string, - overrides: Record, - isRetry = false - ): Promise { - try { - const resp = await this.fileUpload.analyzeFile(fileContents, overrides); - const serverSettings = processResults(resp); - - return { - fileContents, - results: resp.results, - explanation: resp.results.explanation, - serverSettings, - analysisStatus: STATUS.COMPLETED, - }; - } catch (e) { - return { - fileContents, - results: null, - explanation: undefined, - serverSettings: null, - analysisError: e, - analysisStatus: STATUS.FAILED, - }; - } - } - - private setStatus(status: Partial) { - this.analyzedFile$.next({ - ...this.getStatus(), - ...status, - }); - } - - public getStatus() { - return this.analyzedFile$.getValue(); - } - - public getFileName() { - return this.analyzedFile$.getValue().fileName; - } - public getMappings() { - return this.analyzedFile$.getValue().results?.mappings; - } - public getPipeline(): IngestPipeline | undefined { - return this.analyzedFile$.getValue().results?.ingest_pipeline; - } - public getFormat() { - return this.analyzedFile$.getValue().results?.format; - } - public getData() { - return this.analyzedFile$.getValue().data; - } - - public async import(index: string, mappings: MappingTypeMapping, pipelineId: string | undefined) { - this.setStatus({ importStatus: STATUS.STARTED }); - const format = this.analyzedFile$.getValue().results!.format; - const importer = await this.fileUpload.importerFactory(format, { - excludeLinesPattern: this.analyzedFile$.getValue().results!.exclude_lines_pattern, - multilineStartPattern: this.analyzedFile$.getValue().results!.multiline_start_pattern, - }); - - const ingestPipeline = this.getPipeline(); - importer.initializeWithoutCreate(index, mappings, ingestPipeline ? [ingestPipeline] : []); - const data = this.getData(); - if (data === null) { - this.setStatus({ importStatus: STATUS.FAILED }); - return; - } - importer.read(data); - try { - const resp = await importer.import(index, pipelineId, (p) => { - this.setStatus({ importProgress: p }); - }); - this.setStatus({ docCount: resp.docCount, importStatus: STATUS.COMPLETED }); - return resp; - } catch (error) { - this.setStatus({ importStatus: STATUS.FAILED }); - return; - } - } -} diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/index.ts b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/index.ts deleted file mode 100644 index 3b690290f9f6b..0000000000000 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { FileManager } from './file_manager'; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/merge_tools.test.ts b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/merge_tools.test.ts deleted file mode 100644 index 8cd0c00f5d232..0000000000000 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/merge_tools.test.ts +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - createMergedMappings, - getMappingClashInfo, - getFormatClashes, - CLASH_TYPE, -} from './merge_tools'; -import type { FileWrapper, FileAnalysis } from './file_wrapper'; - -describe('merge_tools', () => { - describe('createMergedMappings', () => { - it('should return merged mappings when all mappings are the same', () => { - const files = [ - { - getMappings: () => ({ properties: { field1: { type: 'text' } } }), - } as never as FileWrapper, - { - getMappings: () => ({ properties: { field1: { type: 'text' } } }), - } as never as FileWrapper, - ]; - - const result = createMergedMappings(files); - expect(result.mergedMappings).toEqual({ properties: { field1: { type: 'text' } } }); - expect(result.mappingClashes).toEqual([]); - }); - - it('should return merged mappings when a keyword mapping is being merged with a text mapping', () => { - const files = [ - { - getMappings: () => ({ properties: { field1: { type: 'text' } } }), - } as never as FileWrapper, - { - getMappings: () => ({ properties: { field1: { type: 'keyword' } } }), - } as never as FileWrapper, - ]; - - const result = createMergedMappings(files); - expect(result.mergedMappings).toEqual({ properties: { field1: { type: 'text' } } }); - expect(result.mappingClashes).toEqual([]); - }); - - it('should return merged mappings when a text mapping is being merged with a keyword mapping', () => { - const files = [ - { - getMappings: () => ({ properties: { field1: { type: 'keyword' } } }), - } as never as FileWrapper, - { - getMappings: () => ({ properties: { field1: { type: 'text' } } }), - } as never as FileWrapper, - ]; - - const result = createMergedMappings(files); - expect(result.mergedMappings).toEqual({ properties: { field1: { type: 'text' } } }); - expect(result.mappingClashes).toEqual([]); - }); - - it('should return mapping clashes when mappings are different', () => { - const files = [ - { - getFileName: () => 'file1', - getMappings: () => ({ properties: { field1: { type: 'text' } } }), - } as never as FileWrapper, - { - getFileName: () => 'file2', - getMappings: () => ({ properties: { field1: { type: 'number' } } }), - } as never as FileWrapper, - ]; - - const result = createMergedMappings(files); - expect(result.mergedMappings).toEqual({ properties: { field1: { type: 'text' } } }); - expect(result.mappingClashes).toEqual([ - { - fieldName: 'field1', - existingType: 'text', - clashingType: { fileName: 'file2', newType: 'number', fileIndex: 1 }, - }, - ]); - }); - - it('should return mapping clashes when first field doesn`t match the others', () => { - const files = [ - { - getFileName: () => 'file1', - getMappings: () => ({ properties: { field1: { type: 'text' } } }), - } as never as FileWrapper, - { - getFileName: () => 'file2', - getMappings: () => ({ properties: { field1: { type: 'number' } } }), - } as never as FileWrapper, - { - getFileName: () => 'file3', - getMappings: () => ({ properties: { field1: { type: 'number' } } }), - } as never as FileWrapper, - ]; - - const result = createMergedMappings(files); - - expect(result.mergedMappings).toEqual({ properties: { field1: { type: 'text' } } }); - expect(result.mappingClashes).toEqual([ - { - fieldName: 'field1', - existingType: 'text', - clashingType: { - fileName: 'file2', - newType: 'number', - fileIndex: 1, - }, - }, - { - fieldName: 'field1', - existingType: 'text', - clashingType: { - fileName: 'file3', - newType: 'number', - fileIndex: 2, - }, - }, - ]); - }); - }); - - describe('getMappingClashInfo', () => { - it('should return file clashes based on mapping clashes', () => { - const mappingClashes = [ - { - fieldName: 'field1', - existingType: 'text', - clashingType: { - fileName: 'file2', - newType: 'number', - fileIndex: 1, - }, - }, - { - fieldName: 'field1', - existingType: 'text', - clashingType: { - fileName: 'file3', - newType: 'number', - fileIndex: 2, - }, - }, - ]; - - const filesStatus = [ - { fileName: 'file1', supportedFormat: true } as FileAnalysis, - { fileName: 'file2', supportedFormat: true } as FileAnalysis, - { fileName: 'file3', supportedFormat: true } as FileAnalysis, - ]; - - const result = getMappingClashInfo(mappingClashes, filesStatus); - - expect(result).toEqual([ - { fileName: 'file1', clash: true, clashType: CLASH_TYPE.MAPPING }, - { fileName: 'file2', clash: false, clashType: CLASH_TYPE.MAPPING }, - { fileName: 'file3', clash: false, clashType: CLASH_TYPE.MAPPING }, - ]); - }); - }); - - describe('getFormatClashes', () => { - it('should return no clashes when all formats are supported', () => { - const files = [ - { - getFormat: () => 'semi_structured_text', - getFileName: () => 'file1', - getStatus: () => ({ supportedFormat: true }), - } as FileWrapper, - { - getFormat: () => 'semi_structured_text', - getFileName: () => 'file2', - getStatus: () => ({ supportedFormat: true }), - } as FileWrapper, - ]; - - const result = getFormatClashes(files); - expect(result).toEqual([ - { fileName: 'file1', clash: false }, - { fileName: 'file2', clash: false }, - ]); - }); - - it('should return format clashes when formats are different', () => { - const files = [ - { - getFormat: () => 'semi_structured_text', - getFileName: () => 'file1', - getStatus: () => ({ supportedFormat: true }), - } as FileWrapper, - { - getFormat: () => 'delimited', - getFileName: () => 'file2', - getStatus: () => ({ supportedFormat: true }), - } as FileWrapper, - ]; - - const result = getFormatClashes(files); - expect(result).toEqual([ - { fileName: 'file1', clash: true, clashType: CLASH_TYPE.FORMAT }, - { fileName: 'file2', clash: true, clashType: CLASH_TYPE.FORMAT }, - ]); - }); - - it('should choose the correct file that clashes', () => { - const files = [ - { - getFormat: () => 'semi_structured_text', - getFileName: () => 'file1', - getStatus: () => ({ supportedFormat: true }), - } as FileWrapper, - { - getFormat: () => 'semi_structured_text', - getFileName: () => 'file2', - getStatus: () => ({ supportedFormat: true }), - } as FileWrapper, - { - getFormat: () => 'delimited', - getFileName: () => 'file3', - getStatus: () => ({ supportedFormat: true }), - } as FileWrapper, - ]; - - const result = getFormatClashes(files); - - expect(result).toEqual([ - { fileName: 'file1', clash: false, clashType: undefined }, - { fileName: 'file2', clash: false, clashType: undefined }, - { fileName: 'file3', clash: true, clashType: 1 }, - ]); - }); - - it('should return unsupported format clashes', () => { - const files = [ - { - getFormat: () => 'semi_structured_text', - getFileName: () => 'file1', - getStatus: () => ({ supportedFormat: true }), - getMappings: () => ({ properties: { field1: { type: 'text' } } }), - } as unknown as FileWrapper, - { - getFormat: () => 'semi_structured_text', - getFileName: () => 'file2', - getStatus: () => ({ supportedFormat: true }), - getMappings: () => ({ properties: { field1: { type: 'text' } } }), - } as unknown as FileWrapper, - { - getFormat: () => 'unknown', - getFileName: () => 'file3', - getStatus: () => ({ supportedFormat: false }), - getMappings: () => ({ properties: { field1: { type: 'text' } } }), - } as unknown as FileWrapper, - ]; - - const result = getFormatClashes(files); - - expect(result).toEqual([ - { fileName: 'file1', clash: false, clashType: undefined }, - { fileName: 'file2', clash: false, clashType: undefined }, - { fileName: 'file3', clash: true, clashType: CLASH_TYPE.UNSUPPORTED }, - ]); - }); - }); -}); diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/merge_tools.ts b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/merge_tools.ts deleted file mode 100644 index b53df642749df..0000000000000 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_manager/merge_tools.ts +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { FileAnalysis, FileWrapper } from './file_wrapper'; - -export enum CLASH_TYPE { - MAPPING, - FORMAT, - UNSUPPORTED, -} - -export interface MappingClash { - fieldName: string; - existingType: string; - clashingType: { fileName: string; newType: string; fileIndex: number }; -} - -export interface FileClash { - fileName: string; - clash: boolean; - clashType?: CLASH_TYPE; -} - -export function createMergedMappings(files: FileWrapper[]) { - const mappings = files.map((file) => file.getMappings() ?? { properties: {} }); - - // stringify each mappings and see if they are the same, if so return the first one. - // otherwise drill down and extract each field with it's type. - const mappingsString = mappings.map((m) => JSON.stringify(m)); - if (mappingsString.every((m) => m === mappingsString[0])) { - return { mergedMappings: mappings[0], mappingClashes: [] }; - } - - const fieldsPerFile = mappings.map((m) => { - if (m.properties === undefined) { - return []; - } - return Object.entries(m.properties) - .map(([key, value]) => { - return { name: key, value }; - }) - .sort((a, b) => a.name.localeCompare(b.name)); - }); - - const mappingClashes: MappingClash[] = []; - - const mergedMappingsMap = fieldsPerFile.reduce((acc, fields, i) => { - fields.forEach((field) => { - if (!acc.has(field.name)) { - acc.set(field.name, field.value); - } else { - const existingField = acc.get(field.name); - - if (existingField.type !== field.value.type) { - // if either new or existing field is text or keyword, we should allow the clash - // and replace the existing field with the new field if the existing is keyword = - if (existingField.type === 'keyword' && field.value.type === 'text') { - // the existing field is keyword and the new field is text, replace the existing field with the text version - acc.set(field.name, field.value); - } else if (existingField.type === 'text' && field.value.type === 'keyword') { - // do nothing - } else { - mappingClashes.push({ - fieldName: field.name, - existingType: existingField.type, - clashingType: { - fileName: files[i].getFileName(), - newType: field.value.type, - fileIndex: i, - }, - }); - } - } - } - }); - return acc; - }, new Map()); - - const mergedMappings = { - properties: Object.fromEntries(mergedMappingsMap), - }; - - return { mergedMappings, mappingClashes }; -} - -export function getMappingClashInfo( - mappingClashes: MappingClash[], - filesStatus: FileAnalysis[] -): FileClash[] { - const clashCounts = filesStatus - .reduce>((acc, file, i) => { - const ff = { index: i, count: 0 }; - mappingClashes.forEach((clash) => { - if (clash.clashingType.fileIndex === i) { - ff.count++; - } - }); - acc.push(ff); - return acc; - }, []) - .sort((a, b) => b.count - a.count); - - const middleIndex = Math.floor(clashCounts.length / 2); - - const median = clashCounts[middleIndex].count; - - const medianAboveZero = median > 0; - const zeroCount = clashCounts.filter((c) => c.count === 0).length; - const aboveZeroCount = clashCounts.length - zeroCount; - const allClash = zeroCount === aboveZeroCount; - - clashCounts.sort((a, b) => a.index - b.index); - - return clashCounts.map((c) => { - return { - fileName: filesStatus[c.index].fileName, - clash: - allClash || - (medianAboveZero === false && c.count > 0) || - (medianAboveZero && c.count === 0), - clashType: CLASH_TYPE.MAPPING, - }; - }); -} - -export function getFormatClashes(files: FileWrapper[]): FileClash[] { - const formatMap = files - .map((file) => file.getFormat()) - .reduce((acc, format, i) => { - if (format === undefined) { - acc.set('unknown', (acc.get('unknown') ?? 0) + 1); - } else { - acc.set(format, (acc.get(format) ?? 0) + 1); - } - return acc; - }, new Map()); - - // return early if there is only one format and it is supported - if (formatMap.size === 1 && formatMap.has('unknown') === false) { - return files.map((f) => ({ - fileName: f.getFileName(), - clash: false, - })); - } - - const formatArray = Array.from(formatMap.entries()).sort((a, b) => b[1] - a[1]); - - const fileClashes = files.map((f) => { - return { - fileName: f.getFileName(), - clash: f.getStatus().supportedFormat === false, - clashType: CLASH_TYPE.UNSUPPORTED, - }; - }); - - const topCount = formatArray[0]; - - // if the top count is for an unsupported format, - // return the fileClashes containing unsupported formats - if (topCount[0] === 'unknown') { - return fileClashes; - } - - // Check if all counts are the same, - // mark all files as clashing - const counts = Array.from(formatMap.values()); - const allCountsSame = counts.every((count) => count === counts[0]); - if (allCountsSame) { - return files.map((f) => { - return { - fileName: f.getFileName(), - clash: true, - clashType: f.getStatus().supportedFormat ? CLASH_TYPE.FORMAT : CLASH_TYPE.UNSUPPORTED, - }; - }); - } - - return files.map((f) => { - let clashType: CLASH_TYPE | undefined; - if (f.getStatus().supportedFormat === false) { - clashType = CLASH_TYPE.UNSUPPORTED; - } else if (f.getFormat() !== topCount[0]) { - clashType = CLASH_TYPE.FORMAT; - } - - return { - fileName: f.getFileName(), - clash: clashType !== undefined, - clashType, - }; - }); -} diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_picker.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_picker.tsx index bc80739bdc2d6..c3be4700f690e 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_picker.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_picker.tsx @@ -5,59 +5,63 @@ * 2.0. */ import type { EuiFilePickerProps } from '@elastic/eui'; -import { EuiFormRow, EuiFilePicker, EuiSpacer } from '@elastic/eui'; +import { EuiFormRow, EuiFilePicker, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import type { EuiFilePickerClass } from '@elastic/eui/src/components/form/file_picker/file_picker'; +import type { FileUploadManager } from '@kbn/file-upload-common'; import { i18n } from '@kbn/i18n'; import type { FC } from 'react'; import React, { useCallback, useRef } from 'react'; -import type { FileManager } from './file_manager'; interface Props { - fileManager: FileManager; + fileUploadManager: FileUploadManager; + fullWidth?: boolean; } -export const FilePicker: FC = ({ fileManager: fm }) => { +export const FilePicker: FC = ({ fileUploadManager, fullWidth }) => { const filePickerRef = useRef(null); const onFilePickerChange = useCallback( async (files: FileList | null) => { if (files && files.length > 0) { - await fm.addFiles(files); + await fileUploadManager.addFiles(files); // Clear the file picker after adding files filePickerRef.current?.removeFiles(); } }, - [fm] + [fileUploadManager] ); return ( - <> - - >} - id="filePicker" + + {fullWidth === false ? : null} + + onFilePickerChange(files)} - /> - - - + > + >} + id="filePicker" + fullWidth + display="large" + compressed + multiple + initialPromptText={i18n.translate( + 'xpack.dataVisualizer.file.filePicker.selectOrDragAndDropFiles', + { + defaultMessage: 'Select or drag and drop files', + } + )} + onChange={(files) => onFilePickerChange(files)} + /> + + + {fullWidth === false ? : null} + ); }; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_status.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_status.tsx index 030d12531808a..4a2689c5fd14e 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_status.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_status.tsx @@ -21,9 +21,8 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { FileAnalysis } from './file_manager/file_wrapper'; -import { STATUS, type UploadStatus } from './file_manager/file_manager'; -import { CLASH_TYPE } from './file_manager/merge_tools'; +import type { UploadStatus, FileAnalysis } from '@kbn/file-upload-common'; +import { STATUS, CLASH_TYPE } from '@kbn/file-upload-common/src/file_manager'; import { AnalysisSummary } from '../application/file_data_visualizer/components/analysis_summary'; import { FileContents } from '../application/file_data_visualizer/components/file_contents'; @@ -95,7 +94,7 @@ export const FileStatus: FC = ({ {fileStatus.fileName}  - {fileStatus.fileSize} + {fileStatus.fileSizeInfo.fileSize} diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite.tsx index 7ec17d169a539..f6c43d66aeeed 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite.tsx @@ -5,51 +5,76 @@ * 2.0. */ import type { FC, PropsWithChildren } from 'react'; -import React from 'react'; +import React, { useMemo } from 'react'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; -import type { IndicesIndexSettings } from '@elastic/elasticsearch/lib/api/types'; -import type { FileUploadResults } from '@kbn/file-upload-common'; -import type { FlyoutContent } from '@kbn/file-upload-common/src/types'; +import { + useFileUpload, + type OpenFileUploadLiteContext, + FileUploadContext, +} from '@kbn/file-upload-common'; +import type { CoreStart } from '@kbn/core/public'; +import { FileUploadManager } from '@kbn/file-upload-common/src/file_manager'; import type { ResultLinks } from '../../common/app'; -import type { GetAdditionalLinks } from '../application/common/components/results_links'; -import { getCoreStart, getPluginsStart } from '../kibana_services'; import { FileUploadLiteView } from './file_upload_lite_view'; +import type { GetAdditionalLinks } from '../application/common/components/results_links'; +import type { DataVisualizerStartDependencies } from '../application/common/types/data_visualizer_plugin'; export interface Props { + coreStart: CoreStart; + plugins: DataVisualizerStartDependencies; resultLinks?: ResultLinks; getAdditionalLinks?: GetAdditionalLinks; - setUploadResults?: (results: FileUploadResults) => void; - autoAddInference?: string; - autoCreateDataView?: boolean; - indexSettings?: IndicesIndexSettings; - initialIndexName?: string; - flyoutContent?: FlyoutContent; + props: OpenFileUploadLiteContext; onClose?: () => void; } export const FileDataVisualizerLite: FC = ({ + coreStart, + plugins, getAdditionalLinks, resultLinks, - setUploadResults, - autoAddInference, - autoCreateDataView, - indexSettings, - initialIndexName, - flyoutContent, + props, onClose, }) => { - const coreStart = getCoreStart(); - const { data, maps, embeddable, share, fileUpload, cloud, fieldFormats } = getPluginsStart(); const services = { ...coreStart, - data, - maps, - embeddable, - share, - fileUpload, - fieldFormats, + ...plugins, }; + const { data, fileUpload, cloud } = services; + + const { existingIndex, autoAddInference, autoCreateDataView, indexSettings, onUploadComplete } = + props; + const fileUploadManager = useMemo( + () => + new FileUploadManager( + fileUpload, + coreStart.http, + data.dataViews, + autoAddInference ?? null, + autoCreateDataView, + true, + existingIndex ?? null, + indexSettings + ), + [ + autoAddInference, + autoCreateDataView, + coreStart.http, + data.dataViews, + existingIndex, + fileUpload, + indexSettings, + ] + ); + + const fileUploadContextValue = useFileUpload( + fileUploadManager, + data, + coreStart.application, + onUploadComplete, + coreStart.http + ); const EmptyContext: FC> = ({ children }) => <>{children}; const CloudContext = cloud?.CloudContextProvider ?? EmptyContext; @@ -58,21 +83,14 @@ export const FileDataVisualizerLite: FC = ({ - + + + diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite_action.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite_action.tsx index 3365209eec09d..e30e68dc83378 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite_action.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite_action.tsx @@ -50,9 +50,7 @@ export function createOpenFileUploadLiteAction( flyoutContent, }: OpenFileUploadLiteContext) { try { - const { share, data } = plugins; - - createFlyout(coreStart, share, data, { + createFlyout(coreStart, plugins, { onUploadComplete, autoAddInference, autoCreateDataView, diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite_view.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite_view.tsx index c4c1961f33083..8de5b1de4f443 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite_view.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite_view.tsx @@ -19,111 +19,50 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; -import type { ApplicationStart, HttpSetup } from '@kbn/core/public'; -import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import type { FileUploadStartApi } from '@kbn/file-upload-plugin/public/api'; import { FormattedMessage } from '@kbn/i18n-react'; import type { FC } from 'react'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import useObservable from 'react-use/lib/useObservable'; -import type { IndicesIndexSettings } from '@elastic/elasticsearch/lib/api/types'; -import type { FileUploadResults } from '@kbn/file-upload-common'; -import type { FlyoutContent } from '@kbn/file-upload-common/src/types'; +import React, { useEffect } from 'react'; +import { useFileUploadContext } from '@kbn/file-upload-common'; +import type { OpenFileUploadLiteContext } from '@kbn/file-upload-common/src/types'; +import { STATUS } from '@kbn/file-upload-common/src/file_manager'; import type { ResultLinks } from '../../common/app'; import type { GetAdditionalLinks } from '../application/common/components/results_links'; import { FileClashWarning } from './file_clash_warning'; -import { FileManager } from './file_manager'; -import { STATUS } from './file_manager/file_manager'; import { FilePicker } from './file_picker'; import { FileStatus } from './file_status'; import { IndexInput } from './index_input'; import { OverallUploadStatus } from './overall_upload_status'; import { ImportErrors } from './import_errors'; import { DataViewIllustration } from './data_view_illustration'; -import { useDataVisualizerKibana } from '../application/kibana_context'; interface Props { - dataStart: DataPublicPluginStart; - http: HttpSetup; - fileUpload: FileUploadStartApi; resultLinks?: ResultLinks; - capabilities: ApplicationStart['capabilities']; getAdditionalLinks?: GetAdditionalLinks; - setUploadResults?: (results: FileUploadResults) => void; - autoAddInference?: string; - autoCreateDataView?: boolean; - indexSettings?: IndicesIndexSettings; - initialIndexName?: string; - flyoutContent?: FlyoutContent; + props: OpenFileUploadLiteContext; onClose?: () => void; } -export const FileUploadLiteView: FC = ({ - fileUpload, - http, - dataStart, - setUploadResults, - autoAddInference, - autoCreateDataView, - indexSettings, - initialIndexName, - flyoutContent, - onClose, -}) => { +export const FileUploadLiteView: FC = ({ props, onClose }) => { + const { flyoutContent } = props; const { - services: { - application: { navigateToApp }, - }, - } = useDataVisualizerKibana(); - - const [indexName, setIndexName] = useState(''); - const [indexValidationStatus, setIndexValidationStatus] = useState(STATUS.NOT_STARTED); - - const fm = useMemo( - () => - new FileManager( - fileUpload, - http, - dataStart.dataViews, - autoAddInference ?? null, - autoCreateDataView, - true, - indexSettings - ), - [autoAddInference, autoCreateDataView, dataStart.dataViews, fileUpload, http, indexSettings] - ); - const deleteFile = useCallback((i: number) => fm.removeFile(i), [fm]); - - const filesStatus = useObservable(fm.fileAnalysisStatus$, []); - const uploadStatus = useObservable(fm.uploadStatus$, fm.uploadStatus$.getValue()); - const fileClashes = useMemo( - () => uploadStatus.fileClashes.some((f) => f.clash), - [uploadStatus.fileClashes] - ); - - const fullFileUpload = useCallback( - () => navigateToApp('home', { path: '#/tutorial_directory/fileDataViz' }), - [navigateToApp] - ); + fileUploadManager, + filesStatus, + uploadStatus, + fileClashes, + fullFileUpload, + uploadInProgress, + onImportClick, + canImport, + setIndexName, + setIndexValidationStatus, + deleteFile, + } = useFileUploadContext(); useEffect(() => { return () => { - fm.destroy(); + fileUploadManager.destroy(); }; - }, [fm]); - - const uploadInProgress = - uploadStatus.overallImportStatus === STATUS.STARTED || - uploadStatus.overallImportStatus === STATUS.COMPLETED || - uploadStatus.overallImportStatus === STATUS.FAILED; - - const onImportClick = useCallback(() => { - fm.import(indexName).then((res) => { - if (setUploadResults && res) { - setUploadResults(res); - } - }); - }, [fm, indexName, setUploadResults]); + }, [fileUploadManager]); return ( <> @@ -179,7 +118,7 @@ export const FileUploadLiteView: FC = ({ - + ) : null} @@ -201,7 +140,7 @@ export const FileUploadLiteView: FC = ({ fm.removeClashingFiles()} + removeClashingFiles={() => fileUploadManager.removeClashingFiles()} /> ) : null} @@ -210,16 +149,17 @@ export const FileUploadLiteView: FC = ({ {uploadStatus.overallImportStatus === STATUS.NOT_STARTED && filesStatus.length > 0 && - uploadStatus.analysisOk ? ( + uploadStatus.analysisStatus !== STATUS.NOT_STARTED ? ( <> - + {fileUploadManager.isExistingIndexUpload() === false ? ( + + ) : null} ) : null} + {uploadInProgress ? ( <> @@ -270,10 +210,7 @@ export const FileUploadLiteView: FC = ({ ) : null} {uploadStatus.overallImportStatus === STATUS.NOT_STARTED ? ( - + import('../file_upload_lite')); - -export const FileDataVisualizerLiteWrapper: FC<{ - resultLinks?: ResultLinks; - setUploadResults?: (results: FileUploadResults) => void; - autoAddInference?: string; - autoCreateDataView?: boolean; - indexSettings?: IndicesIndexSettings; - initialIndexName?: string; - flyoutContent?: FlyoutContent; - onClose?: () => void; -}> = ({ - resultLinks, - setUploadResults, - autoAddInference, - autoCreateDataView, - indexSettings, - initialIndexName, - flyoutContent, - onClose, -}) => { - return ( - }> - - - ); -}; - -export function getFileDataVisualizerLiteWrapper( - resultLinks?: ResultLinks, - setUploadResults?: (results: FileUploadResults) => void, - autoAddInference?: string, - autoCreateDataView?: boolean, - indexSettings?: IndicesIndexSettings, - initialIndexName?: string, - flyoutContent?: FlyoutContent, - onClose?: () => void -) { - return ( - - ); -} diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/flyout/create_flyout.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/flyout/create_flyout.tsx index ed3b4a5183f5d..57615bc0cb170 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/flyout/create_flyout.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/flyout/create_flyout.tsx @@ -10,17 +10,15 @@ import React, { Suspense, lazy } from 'react'; import { takeUntil, distinctUntilChanged, skip } from 'rxjs'; import { from } from 'rxjs'; import { toMountPoint } from '@kbn/react-kibana-mount'; -import type { SharePluginStart } from '@kbn/share-plugin/public'; -import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { CoreStart } from '@kbn/core/public'; import type { FileUploadResults, OpenFileUploadLiteContext } from '@kbn/file-upload-common'; import { EuiFlyoutHeader, EuiSkeletonText, EuiSpacer, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import type { DataVisualizerStartDependencies } from '../../application/common/types/data_visualizer_plugin'; export function createFlyout( coreStart: CoreStart, - share: SharePluginStart, - data: DataPublicPluginStart, + plugins: DataVisualizerStartDependencies, props: OpenFileUploadLiteContext ) { const { @@ -31,26 +29,18 @@ export function createFlyout( } = coreStart; const LazyFlyoutContents = lazy(async () => { - const { FlyoutContents } = await import('./flyout_contents'); + const { FileDataVisualizerLite } = await import('../file_upload_lite'); return { - default: FlyoutContents, + default: FileDataVisualizerLite, }; }); let results: FileUploadResults | null = null; - const { - onUploadComplete, - autoAddInference, - autoCreateDataView, - indexSettings, - initialIndexName, - flyoutContent, - } = props; const onFlyoutClose = () => { flyoutSession.close(); - if (results !== null && typeof onUploadComplete === 'function') { - onUploadComplete(results); + if (results !== null && typeof props.onUploadComplete === 'function') { + props.onUploadComplete(results); } }; @@ -59,21 +49,16 @@ export function createFlyout( }> { - if (res) { - results = res; - } + ...props, + onUploadComplete: (res) => { + if (res) { + results = res; + } + }, }} + onClose={onFlyoutClose} /> , startServices diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/flyout/flyout.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/flyout/flyout.tsx deleted file mode 100644 index 130b12b0d3bc4..0000000000000 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/flyout/flyout.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { FC } from 'react'; -import React from 'react'; - -import type { IndicesIndexSettings } from '@elastic/elasticsearch/lib/api/types'; -import type { FileUploadResults } from '@kbn/file-upload-common'; -import type { FlyoutContent } from '@kbn/file-upload-common/src/types'; -import { getFileDataVisualizerLiteWrapper } from './component_wrapper'; - -interface Props { - onClose?: () => void; - setUploadResults?: (results: FileUploadResults) => void; - autoAddInference?: string; - autoCreateDataView?: boolean; - indexSettings?: IndicesIndexSettings; - initialIndexName?: string; - flyoutContent?: FlyoutContent; -} - -export const FileUploadLiteFlyoutContents: FC = ({ - onClose, - setUploadResults, - autoAddInference, - autoCreateDataView, - indexSettings, - initialIndexName, - flyoutContent, -}) => { - const Wrapper = getFileDataVisualizerLiteWrapper( - undefined, - setUploadResults, - autoAddInference, - autoCreateDataView, - indexSettings, - initialIndexName, - flyoutContent, - onClose - ); - return <>{Wrapper}; -}; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/flyout/flyout_contents.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/flyout/flyout_contents.tsx deleted file mode 100644 index fb4505fc62158..0000000000000 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/flyout/flyout_contents.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import type { FC } from 'react'; -import type { CoreStart } from '@kbn/core/public'; -import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import type { FileUploadResults, OpenFileUploadLiteContext } from '@kbn/file-upload-common'; -import type { SharePluginStart } from '@kbn/share-plugin/public'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { FileUploadLiteFlyoutContents } from './flyout'; - -interface Props { - coreStart: CoreStart; - share: SharePluginStart; - data: DataPublicPluginStart; - props: OpenFileUploadLiteContext; - onFlyoutClose: () => void; - setUploadResults: (results: FileUploadResults) => void; -} - -export const FlyoutContents: FC = ({ - coreStart, - share, - data, - props: { autoAddInference, autoCreateDataView, indexSettings, initialIndexName, flyoutContent }, - onFlyoutClose, - setUploadResults, -}) => { - return ( - - { - onFlyoutClose(); - }} - setUploadResults={setUploadResults} - /> - - ); -}; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/import_errors.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/import_errors.tsx index f571ef901d148..edc4f0fee8a03 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/import_errors.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/import_errors.tsx @@ -6,9 +6,9 @@ */ import { EuiCallOut, EuiSpacer } from '@elastic/eui'; +import type { UploadStatus } from '@kbn/file-upload-common'; import type { FC } from 'react'; import React from 'react'; -import { type UploadStatus } from './file_manager/file_manager'; interface Props { uploadStatus: UploadStatus; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/index_input.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/index_input.tsx index 4aa4eaeef7c29..047b91ac369a3 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/index_input.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/index_input.tsx @@ -9,24 +9,26 @@ import { EuiFieldText, EuiFormRow } from '@elastic/eui'; import type { FC } from 'react'; import React, { useState } from 'react'; import useDebounce from 'react-use/lib/useDebounce'; -import type { FileUploadStartApi } from '@kbn/file-upload-plugin/public/api'; import { i18n } from '@kbn/i18n'; import useMountedState from 'react-use/lib/useMountedState'; -import { STATUS } from './file_manager/file_manager'; +import { STATUS } from '@kbn/file-upload-common/src/file_manager'; +import { useDataVisualizerKibana } from '../application/kibana_context'; interface Props { setIndexName: (name: string) => void; setIndexValidationStatus: (status: STATUS) => void; - fileUpload: FileUploadStartApi; initialIndexName?: string; } export const IndexInput: FC = ({ setIndexName, setIndexValidationStatus, - fileUpload, initialIndexName, }) => { + const { + services: { fileUpload }, + } = useDataVisualizerKibana(); + const [indexNameLocal, setIndexNameLocal] = useState(initialIndexName ?? ''); const [indexNameError, setIndexNameError] = useState(''); const isMounted = useMountedState(); diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/overall_upload_progress.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/overall_upload_progress.tsx index 1b66906e96b72..ea347c80a0fcd 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/overall_upload_progress.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/overall_upload_progress.tsx @@ -5,9 +5,9 @@ * 2.0. */ import { EuiProgress } from '@elastic/eui'; +import type { FileAnalysis } from '@kbn/file-upload-common'; import type { FC } from 'react'; import React from 'react'; -import type { FileAnalysis } from './file_manager/file_wrapper'; interface Props { filesStatus: FileAnalysis[]; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/overall_upload_status.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/overall_upload_status.tsx index a18888cd9922d..aadb890ab0c2d 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/overall_upload_status.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/overall_upload_status.tsx @@ -11,9 +11,8 @@ import type { EuiStepStatus } from '@elastic/eui'; import { EuiSpacer, EuiSteps } from '@elastic/eui'; import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; import { i18n } from '@kbn/i18n'; -import type { FileAnalysis } from './file_manager/file_wrapper'; -import { STATUS } from './file_manager/file_manager'; -import { type UploadStatus } from './file_manager/file_manager'; +import type { UploadStatus, FileAnalysis } from '@kbn/file-upload-common'; +import { STATUS } from '@kbn/file-upload-common/src/file_manager'; import { FileStatus } from './file_status'; interface Props { From 5fe99dbb599ac602e109f36a73b109c8c899c22b Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 21 May 2025 16:26:37 +0100 Subject: [PATCH 05/16] plugin refactor --- package.json | 1 + tsconfig.base.json | 2 + .../shared/file-upload-common/index.ts | 11 +- .../file-upload-common/src/constants.ts | 10 +- .../shared/file-upload-common/tsconfig.json | 1 - .../packages/shared/file-upload/README.md | 3 + .../file_upload_manager}/auto_deploy.ts | 0 .../file_upload_manager}/file_manager.ts | 6 +- .../file_upload_manager}/file_size_check.ts | 2 +- .../file_upload_manager}/file_wrapper.ts | 2 +- .../file_upload_manager}/index.ts | 0 .../file_upload_manager}/merge_tools.test.ts | 0 .../file_upload_manager}/merge_tools.ts | 0 .../file_upload_manager}/tika_analyzer.ts | 2 +- .../file_upload_manager}/tika_utils.ts | 0 .../packages/shared/file-upload/index.ts | 17 +++ .../shared/file-upload/jest.config.js | 12 ++ .../packages/shared/file-upload/kibana.jsonc | 7 ++ .../packages/shared/file-upload/package.json | 6 + .../src/use_file_upload.ts | 5 +- .../src/utils.ts | 2 +- .../packages/shared/file-upload/tsconfig.json | 25 ++++ .../data_visualizer/common/constants.ts | 30 ----- .../common/components/utils/utils.ts | 2 +- .../analysis_summary/analysis_summary.tsx | 2 +- .../components/edit_flyout/options/options.ts | 3 +- .../file_contents/file_contents.tsx | 6 +- .../file_size_check.ts | 2 +- .../tika_analyzer.ts | 2 +- .../import_settings/semantic_text_info.tsx | 2 +- .../components/results_view/results_view.tsx | 2 +- .../public/lite/file_clash_warning.tsx | 4 +- .../public/lite/file_picker.tsx | 2 +- .../public/lite/file_status.tsx | 4 +- .../public/lite/file_upload_lite.tsx | 8 +- .../public/lite/file_upload_lite_view.tsx | 4 +- .../public/lite/import_errors.tsx | 2 +- .../public/lite/index_input.tsx | 2 +- .../public/lite/overall_upload_progress.tsx | 2 +- .../public/lite/overall_upload_status.tsx | 4 +- .../private/data_visualizer/tsconfig.json | 1 + .../private/file_upload/common/constants.ts | 29 ----- .../private/file_upload/common/index.ts | 2 - .../geo_upload_form/geo_file_picker.tsx | 2 +- .../geo/abstract_geo_file_importer.tsx | 2 +- .../public/importer/get_max_bytes.ts | 5 +- .../file_upload/public/importer/importer.ts | 3 +- .../public/importer/importer_factory.ts | 2 +- .../private/file_upload/server/import_data.ts | 3 +- .../private/file_upload/server/plugin.ts | 2 +- .../server/preview_tika_contents.ts | 2 +- .../private/file_upload/server/routes.ts | 5 +- .../plugins/private/file_upload/tsconfig.json | 1 + .../translations/translations/fr-FR.json | 85 +++++++------- .../translations/translations/ja-JP.json | 69 +++++------ .../translations/translations/zh-CN.json | 109 +++++++++--------- yarn.lock | 4 + 57 files changed, 265 insertions(+), 258 deletions(-) create mode 100644 x-pack/platform/packages/shared/file-upload/README.md rename x-pack/platform/packages/shared/{file-upload-common/src/file_manager => file-upload/file_upload_manager}/auto_deploy.ts (100%) rename x-pack/platform/packages/shared/{file-upload-common/src/file_manager => file-upload/file_upload_manager}/file_manager.ts (99%) rename x-pack/platform/packages/shared/{file-upload-common/src/file_manager => file-upload/file_upload_manager}/file_size_check.ts (94%) rename x-pack/platform/packages/shared/{file-upload-common/src/file_manager => file-upload/file_upload_manager}/file_wrapper.ts (99%) rename x-pack/platform/packages/shared/{file-upload-common/src/file_manager => file-upload/file_upload_manager}/index.ts (100%) rename x-pack/platform/packages/shared/{file-upload-common/src/file_manager => file-upload/file_upload_manager}/merge_tools.test.ts (100%) rename x-pack/platform/packages/shared/{file-upload-common/src/file_manager => file-upload/file_upload_manager}/merge_tools.ts (100%) rename x-pack/platform/packages/shared/{file-upload-common/src/file_manager => file-upload/file_upload_manager}/tika_analyzer.ts (98%) rename x-pack/platform/packages/shared/{file-upload-common/src/file_manager => file-upload/file_upload_manager}/tika_utils.ts (100%) create mode 100644 x-pack/platform/packages/shared/file-upload/index.ts create mode 100644 x-pack/platform/packages/shared/file-upload/jest.config.js create mode 100644 x-pack/platform/packages/shared/file-upload/kibana.jsonc create mode 100644 x-pack/platform/packages/shared/file-upload/package.json rename x-pack/platform/packages/shared/{file-upload-common => file-upload}/src/use_file_upload.ts (97%) rename x-pack/platform/packages/shared/{file-upload-common => file-upload}/src/utils.ts (98%) create mode 100644 x-pack/platform/packages/shared/file-upload/tsconfig.json delete mode 100644 x-pack/platform/plugins/private/file_upload/common/constants.ts diff --git a/package.json b/package.json index d5c62a642de0d..744a7202af09a 100644 --- a/package.json +++ b/package.json @@ -547,6 +547,7 @@ "@kbn/field-types": "link:src/platform/packages/shared/kbn-field-types", "@kbn/field-utils": "link:src/platform/packages/shared/kbn-field-utils", "@kbn/fields-metadata-plugin": "link:x-pack/platform/plugins/shared/fields_metadata", + "@kbn/file-upload": "link:x-pack/platform/packages/shared/file-upload", "@kbn/file-upload-common": "link:x-pack/platform/packages/shared/file-upload-common", "@kbn/file-upload-plugin": "link:x-pack/platform/plugins/private/file_upload", "@kbn/files-example-plugin": "link:examples/files_example", diff --git a/tsconfig.base.json b/tsconfig.base.json index 99ba3a87ed61b..8c7bc693c1eba 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -970,6 +970,8 @@ "@kbn/field-utils/*": ["src/platform/packages/shared/kbn-field-utils/*"], "@kbn/fields-metadata-plugin": ["x-pack/platform/plugins/shared/fields_metadata"], "@kbn/fields-metadata-plugin/*": ["x-pack/platform/plugins/shared/fields_metadata/*"], + "@kbn/file-upload": ["x-pack/platform/packages/shared/file-upload"], + "@kbn/file-upload/*": ["x-pack/platform/packages/shared/file-upload/*"], "@kbn/file-upload-common": ["x-pack/platform/packages/shared/file-upload-common"], "@kbn/file-upload-common/*": ["x-pack/platform/packages/shared/file-upload-common/*"], "@kbn/file-upload-plugin": ["x-pack/platform/plugins/private/file_upload"], diff --git a/x-pack/platform/packages/shared/file-upload-common/index.ts b/x-pack/platform/packages/shared/file-upload-common/index.ts index c669f3b3a5c47..704b5bbda96bc 100644 --- a/x-pack/platform/packages/shared/file-upload-common/index.ts +++ b/x-pack/platform/packages/shared/file-upload-common/index.ts @@ -13,13 +13,6 @@ export { FILE_SIZE_DISPLAY_FORMAT, MB, NO_TIME_FORMAT, + TIKA_PREVIEW_CHARS, + INDEX_META_DATA_CREATED_BY, } from './src/constants'; -export { - FileUploadContext, - useFileUpload, - useFileUploadContext, - UPLOAD_TYPE, -} from './src/use_file_upload'; -export { FileUploadManager } from './src/file_manager/file_manager'; -export type { UploadStatus } from './src/file_manager/file_manager'; -export type { FileAnalysis } from './src/file_manager/file_wrapper'; diff --git a/x-pack/platform/packages/shared/file-upload-common/src/constants.ts b/x-pack/platform/packages/shared/file-upload-common/src/constants.ts index e5fceb2a7df78..cbd6c05c77417 100644 --- a/x-pack/platform/packages/shared/file-upload-common/src/constants.ts +++ b/x-pack/platform/packages/shared/file-upload-common/src/constants.ts @@ -16,7 +16,15 @@ export const FILE_FORMATS = { TIKA: 'tika', }; +export const UI_SETTING_MAX_FILE_SIZE = 'fileUpload:maxFileSize'; +export const MAX_FILE_SIZE = '100MB'; +export const MAX_FILE_SIZE_BYTES = 524288000; // 500MB +export const ABSOLUTE_MAX_FILE_SIZE_BYTES = 1073741274; // 1GB +export const FILE_SIZE_DISPLAY_FORMAT = '0,0.[0] b'; +export const MAX_TIKA_FILE_SIZE_BYTES = 62914560; // 60MB + export const NO_TIME_FORMAT = 'null'; export const MB = Math.pow(2, 20); -export const FILE_SIZE_DISPLAY_FORMAT = '0,0.[0] b'; +export const TIKA_PREVIEW_CHARS = 100000; +export const INDEX_META_DATA_CREATED_BY = 'file-data-visualizer'; diff --git a/x-pack/platform/packages/shared/file-upload-common/tsconfig.json b/x-pack/platform/packages/shared/file-upload-common/tsconfig.json index f508b4a96f82e..0d9466be36cca 100644 --- a/x-pack/platform/packages/shared/file-upload-common/tsconfig.json +++ b/x-pack/platform/packages/shared/file-upload-common/tsconfig.json @@ -14,7 +14,6 @@ "target/**/*" ], "kbn_references": [ - "@kbn/file-upload-plugin", "@kbn/index-management-shared-types", "@kbn/core", "@kbn/data-views-plugin", diff --git a/x-pack/platform/packages/shared/file-upload/README.md b/x-pack/platform/packages/shared/file-upload/README.md new file mode 100644 index 0000000000000..5831b4f6224d2 --- /dev/null +++ b/x-pack/platform/packages/shared/file-upload/README.md @@ -0,0 +1,3 @@ +# @kbn/file-upload + +Functions from performing file upload diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/auto_deploy.ts b/x-pack/platform/packages/shared/file-upload/file_upload_manager/auto_deploy.ts similarity index 100% rename from x-pack/platform/packages/shared/file-upload-common/src/file_manager/auto_deploy.ts rename to x-pack/platform/packages/shared/file-upload/file_upload_manager/auto_deploy.ts diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_manager.ts b/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_manager.ts similarity index 99% rename from x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_manager.ts rename to x-pack/platform/packages/shared/file-upload/file_upload_manager/file_manager.ts index 8e43321143d74..06ef25c63cb73 100644 --- a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_manager.ts +++ b/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_manager.ts @@ -24,7 +24,8 @@ import type { MappingTypeMapping, } from '@elastic/elasticsearch/lib/api/types'; import { i18n } from '@kbn/i18n'; -import type { FileUploadResults } from '../types'; + +import { FileUploadResults } from '@kbn/file-upload-common'; import type { FileAnalysis } from './file_wrapper'; import { FileWrapper } from './file_wrapper'; @@ -35,8 +36,9 @@ import { getFormatClashes, getMappingClashInfo, } from './merge_tools'; -import { createUrlOverrides } from '../utils'; + import { AutoDeploy } from './auto_deploy'; +import { createUrlOverrides } from '../src/utils'; export enum STATUS { NA, diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_size_check.ts b/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_size_check.ts similarity index 94% rename from x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_size_check.ts rename to x-pack/platform/packages/shared/file-upload/file_upload_manager/file_size_check.ts index b968f21a03c6f..6c8b366d3d31a 100644 --- a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_size_check.ts +++ b/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_size_check.ts @@ -7,7 +7,7 @@ import type { FileUploadStartApi } from '@kbn/file-upload-plugin/public/api'; import numeral from '@elastic/numeral'; -import { FILE_SIZE_DISPLAY_FORMAT } from '../constants'; +import { FILE_SIZE_DISPLAY_FORMAT } from '@kbn/file-upload-common'; import { isTikaType } from './tika_utils'; export class FileSizeChecker { diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_wrapper.ts b/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_wrapper.ts similarity index 99% rename from x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_wrapper.ts rename to x-pack/platform/packages/shared/file-upload/file_upload_manager/file_wrapper.ts index efeee33fa8663..c75fd18c2fefc 100644 --- a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/file_wrapper.ts +++ b/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_wrapper.ts @@ -16,11 +16,11 @@ import type { } from '@kbn/file-upload-plugin/common/types'; import type { MappingTypeMapping } from '@elastic/elasticsearch/lib/api/types'; import { isTikaType } from './tika_utils'; -import { processResults, readFile, isSupportedFormat } from '../utils'; import { STATUS } from './file_manager'; import { analyzeTikaFile } from './tika_analyzer'; import { FileSizeChecker } from './file_size_check'; +import { processResults, readFile, isSupportedFormat } from '../src/utils'; interface FileSizeInfo { fileSize: number; diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/index.ts b/x-pack/platform/packages/shared/file-upload/file_upload_manager/index.ts similarity index 100% rename from x-pack/platform/packages/shared/file-upload-common/src/file_manager/index.ts rename to x-pack/platform/packages/shared/file-upload/file_upload_manager/index.ts diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/merge_tools.test.ts b/x-pack/platform/packages/shared/file-upload/file_upload_manager/merge_tools.test.ts similarity index 100% rename from x-pack/platform/packages/shared/file-upload-common/src/file_manager/merge_tools.test.ts rename to x-pack/platform/packages/shared/file-upload/file_upload_manager/merge_tools.test.ts diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/merge_tools.ts b/x-pack/platform/packages/shared/file-upload/file_upload_manager/merge_tools.ts similarity index 100% rename from x-pack/platform/packages/shared/file-upload-common/src/file_manager/merge_tools.ts rename to x-pack/platform/packages/shared/file-upload/file_upload_manager/merge_tools.ts diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/tika_analyzer.ts b/x-pack/platform/packages/shared/file-upload/file_upload_manager/tika_analyzer.ts similarity index 98% rename from x-pack/platform/packages/shared/file-upload-common/src/file_manager/tika_analyzer.ts rename to x-pack/platform/packages/shared/file-upload/file_upload_manager/tika_analyzer.ts index b876aeb62e2ef..e26002f901f19 100644 --- a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/tika_analyzer.ts +++ b/x-pack/platform/packages/shared/file-upload/file_upload_manager/tika_analyzer.ts @@ -5,9 +5,9 @@ * 2.0. */ +import { FILE_FORMATS } from '@kbn/file-upload-common'; import type { AnalysisResult, PreviewTikaResponse } from '@kbn/file-upload-plugin/common/types'; import type { FileUploadStartApi } from '@kbn/file-upload-plugin/public/api'; -import { FILE_FORMATS } from '../constants'; export async function analyzeTikaFile( data: ArrayBuffer, diff --git a/x-pack/platform/packages/shared/file-upload-common/src/file_manager/tika_utils.ts b/x-pack/platform/packages/shared/file-upload/file_upload_manager/tika_utils.ts similarity index 100% rename from x-pack/platform/packages/shared/file-upload-common/src/file_manager/tika_utils.ts rename to x-pack/platform/packages/shared/file-upload/file_upload_manager/tika_utils.ts diff --git a/x-pack/platform/packages/shared/file-upload/index.ts b/x-pack/platform/packages/shared/file-upload/index.ts new file mode 100644 index 0000000000000..a7ce265f0973c --- /dev/null +++ b/x-pack/platform/packages/shared/file-upload/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { + FileUploadContext, + useFileUpload, + useFileUploadContext, + UPLOAD_TYPE, +} from './src/use_file_upload'; +export { FileUploadManager, STATUS } from './file_upload_manager/file_manager'; +export type { UploadStatus } from './file_upload_manager/file_manager'; +export type { FileAnalysis } from './file_upload_manager/file_wrapper'; +export { CLASH_TYPE, CLASH_ERROR_TYPE } from './file_upload_manager/merge_tools'; diff --git a/x-pack/platform/packages/shared/file-upload/jest.config.js b/x-pack/platform/packages/shared/file-upload/jest.config.js new file mode 100644 index 0000000000000..c4e02949ab091 --- /dev/null +++ b/x-pack/platform/packages/shared/file-upload/jest.config.js @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../../..', + roots: ['/x-pack/platform/packages/shared/file-upload'], +}; diff --git a/x-pack/platform/packages/shared/file-upload/kibana.jsonc b/x-pack/platform/packages/shared/file-upload/kibana.jsonc new file mode 100644 index 0000000000000..0498e139783b0 --- /dev/null +++ b/x-pack/platform/packages/shared/file-upload/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-common", + "id": "@kbn/file-upload", + "owner": "@elastic/ml-ui", + "group": "platform", + "visibility": "shared" +} diff --git a/x-pack/platform/packages/shared/file-upload/package.json b/x-pack/platform/packages/shared/file-upload/package.json new file mode 100644 index 0000000000000..a16572d923143 --- /dev/null +++ b/x-pack/platform/packages/shared/file-upload/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/file-upload", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} diff --git a/x-pack/platform/packages/shared/file-upload-common/src/use_file_upload.ts b/x-pack/platform/packages/shared/file-upload/src/use_file_upload.ts similarity index 97% rename from x-pack/platform/packages/shared/file-upload-common/src/use_file_upload.ts rename to x-pack/platform/packages/shared/file-upload/src/use_file_upload.ts index 5e058a3639a66..bb78d07d092b0 100644 --- a/x-pack/platform/packages/shared/file-upload-common/src/use_file_upload.ts +++ b/x-pack/platform/packages/shared/file-upload/src/use_file_upload.ts @@ -11,9 +11,8 @@ import { i18n } from '@kbn/i18n'; import type { Index } from '@kbn/index-management-shared-types/src/types'; import type { ApplicationStart, HttpSetup } from '@kbn/core/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import type { FileUploadResults } from '..'; -import { FileUploadManager, STATUS } from './file_manager/file_manager'; -import { CLASH_ERROR_TYPE } from './file_manager/merge_tools'; +import type { FileUploadResults } from '@kbn/file-upload-common'; +import { CLASH_ERROR_TYPE, FileUploadManager, STATUS } from '../file_upload_manager'; export enum UPLOAD_TYPE { NEW = 'new', diff --git a/x-pack/platform/packages/shared/file-upload-common/src/utils.ts b/x-pack/platform/packages/shared/file-upload/src/utils.ts similarity index 98% rename from x-pack/platform/packages/shared/file-upload-common/src/utils.ts rename to x-pack/platform/packages/shared/file-upload/src/utils.ts index d740dbb71e60a..fe3564d6e034e 100644 --- a/x-pack/platform/packages/shared/file-upload-common/src/utils.ts +++ b/x-pack/platform/packages/shared/file-upload/src/utils.ts @@ -8,7 +8,7 @@ import { isEqual } from 'lodash'; import type { AnalysisResult, InputOverrides } from '@kbn/file-upload-plugin/common'; import type { FormattedOverrides } from '@kbn/file-upload-plugin/common/types'; -import { MB, FILE_FORMATS, NO_TIME_FORMAT } from './constants'; +import { MB, FILE_FORMATS, NO_TIME_FORMAT } from '@kbn/file-upload-common'; export const DEFAULT_LINES_TO_SAMPLE = 1000; const UPLOAD_SIZE_MB = 5; diff --git a/x-pack/platform/packages/shared/file-upload/tsconfig.json b/x-pack/platform/packages/shared/file-upload/tsconfig.json new file mode 100644 index 0000000000000..7ffaaf5da106d --- /dev/null +++ b/x-pack/platform/packages/shared/file-upload/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/file-upload-plugin", + "@kbn/file-upload-common", + "@kbn/index-management-shared-types", + "@kbn/core", + "@kbn/data-views-plugin", + "@kbn/i18n", + "@kbn/data-plugin", + ] +} diff --git a/x-pack/platform/plugins/private/data_visualizer/common/constants.ts b/x-pack/platform/plugins/private/data_visualizer/common/constants.ts index 4dc63e83d5f20..13657e0129d3c 100644 --- a/x-pack/platform/plugins/private/data_visualizer/common/constants.ts +++ b/x-pack/platform/plugins/private/data_visualizer/common/constants.ts @@ -9,36 +9,6 @@ import { i18n } from '@kbn/i18n'; import { KBN_FIELD_TYPES } from '@kbn/field-types'; export const APP_ID = 'data_visualizer'; -export const UI_SETTING_MAX_FILE_SIZE = 'fileUpload:maxFileSize'; - -export const MB = Math.pow(2, 20); -export const MAX_FILE_SIZE = '100MB'; -export const MAX_FILE_SIZE_BYTES = 104857600; // 100MB - -export const ABSOLUTE_MAX_FILE_SIZE_BYTES = 1073741274; // 1GB -export const FILE_SIZE_DISPLAY_FORMAT = '0,0.[0] b'; - -export const NO_TIME_FORMAT = 'null'; - -// Value to use in the Elasticsearch index mapping meta data to identify the -// index as having been created by the File Data Visualizer. -export const INDEX_META_DATA_CREATED_BY = 'file-data-visualizer'; - -export const FILE_FORMATS = { - DELIMITED: 'delimited', - NDJSON: 'ndjson', - SEMI_STRUCTURED_TEXT: 'semi_structured_text', - TIKA: 'tika', -}; - -export function isSupportedFormat(format: string) { - return ( - format === FILE_FORMATS.NDJSON || - format === FILE_FORMATS.DELIMITED || - format === FILE_FORMATS.SEMI_STRUCTURED_TEXT || - format === FILE_FORMATS.TIKA - ); -} export const SUPPORTED_FIELD_TYPES = { BOOLEAN: 'boolean', diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/common/components/utils/utils.ts b/x-pack/platform/plugins/private/data_visualizer/public/application/common/components/utils/utils.ts index 776f687f7732f..7a18c1f2622b0 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/common/components/utils/utils.ts +++ b/x-pack/platform/plugins/private/data_visualizer/public/application/common/components/utils/utils.ts @@ -7,7 +7,7 @@ import { isEqual } from 'lodash'; import type { AnalysisResult, InputOverrides } from '@kbn/file-upload-plugin/common'; -import { MB, FILE_FORMATS, NO_TIME_FORMAT } from '../../../../../common/constants'; +import { MB, FILE_FORMATS, NO_TIME_FORMAT } from '@kbn/file-upload-common'; export const DEFAULT_LINES_TO_SAMPLE = 1000; const UPLOAD_SIZE_MB = 5; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/analysis_summary/analysis_summary.tsx b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/analysis_summary/analysis_summary.tsx index eb193649e6db3..e107ac037b1c3 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/analysis_summary/analysis_summary.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/analysis_summary/analysis_summary.tsx @@ -11,8 +11,8 @@ import React from 'react'; import { EuiTitle, EuiSpacer, EuiDescriptionList } from '@elastic/eui'; import type { FindFileStructureResponse } from '@kbn/file-upload-plugin/common'; +import { FILE_FORMATS } from '@kbn/file-upload-common'; import { getTikaDisplayType } from '../../../../../common/utils/tika_utils'; -import { FILE_FORMATS } from '../../../../../common/constants'; interface Props { results: FindFileStructureResponse; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/edit_flyout/options/options.ts b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/edit_flyout/options/options.ts index 8ff3de07cc81a..ff3fdeb8b2065 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/edit_flyout/options/options.ts +++ b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/edit_flyout/options/options.ts @@ -5,8 +5,7 @@ * 2.0. */ -import { FILE_FORMATS } from '../../../../../../common/constants'; - +import { FILE_FORMATS } from '@kbn/file-upload-common'; import { TIMESTAMP_OPTIONS, DELIMITER_OPTIONS, diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_contents/file_contents.tsx b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_contents/file_contents.tsx index 8634cfffadcfb..d9fb5e8d606c7 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_contents/file_contents.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_contents/file_contents.tsx @@ -19,10 +19,12 @@ import { EuiCallOut, } from '@elastic/eui'; -import { TIKA_PREVIEW_CHARS, type FindFileStructureResponse } from '@kbn/file-upload-plugin/common'; +import { type FindFileStructureResponse } from '@kbn/file-upload-plugin/common'; import useMountedState from 'react-use/lib/useMountedState'; import { i18n } from '@kbn/i18n'; -import { FILE_FORMATS } from '../../../../../common/constants'; + +import { FILE_FORMATS } from '@kbn/file-upload-common'; +import { TIKA_PREVIEW_CHARS } from '@kbn/file-upload-common'; import { EDITOR_MODE, JsonEditor } from '../json_editor'; import { useGrokHighlighter } from './use_text_parser'; import { LINE_LIMIT } from './grok_highlighter'; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_size_check.ts b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_size_check.ts index 494cacf23f64e..f9fd7bd8fd0f1 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_size_check.ts +++ b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_size_check.ts @@ -7,7 +7,7 @@ import type { FileUploadStartApi } from '@kbn/file-upload-plugin/public/api'; import numeral from '@elastic/numeral'; -import { FILE_SIZE_DISPLAY_FORMAT } from '../../../../../common/constants'; +import { FILE_SIZE_DISPLAY_FORMAT } from '@kbn/file-upload-common'; import { isTikaType } from '../../../../../common/utils/tika_utils'; export class FileSizeChecker { diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/tika_analyzer.ts b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/tika_analyzer.ts index 79361711a0b4a..e26002f901f19 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/tika_analyzer.ts +++ b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/tika_analyzer.ts @@ -5,9 +5,9 @@ * 2.0. */ +import { FILE_FORMATS } from '@kbn/file-upload-common'; import type { AnalysisResult, PreviewTikaResponse } from '@kbn/file-upload-plugin/common/types'; import type { FileUploadStartApi } from '@kbn/file-upload-plugin/public/api'; -import { FILE_FORMATS } from '../../../../../common/constants'; export async function analyzeTikaFile( data: ArrayBuffer, diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/import_settings/semantic_text_info.tsx b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/import_settings/semantic_text_info.tsx index cf49c23a539db..bb08ded90a04b 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/import_settings/semantic_text_info.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/import_settings/semantic_text_info.tsx @@ -11,7 +11,7 @@ import React from 'react'; import type { FindFileStructureResponse } from '@kbn/file-upload-plugin/common'; import { EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { FILE_FORMATS } from '../../../../../common/constants'; +import { FILE_FORMATS } from '@kbn/file-upload-common'; interface Props { results: FindFileStructureResponse; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/results_view/results_view.tsx b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/results_view/results_view.tsx index d31e4b48eca30..69f0426ab2e57 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/results_view/results_view.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/results_view/results_view.tsx @@ -21,7 +21,7 @@ import { } from '@elastic/eui'; import type { FindFileStructureResponse } from '@kbn/file-upload-plugin/common'; -import { FILE_FORMATS } from '../../../../../common/constants'; +import { FILE_FORMATS } from '@kbn/file-upload-common'; import { FileContents } from '../file_contents'; import { AnalysisSummary } from '../analysis_summary'; import { FieldsStatsGrid } from '../../../common/components/fields_stats_grid'; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_clash_warning.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_clash_warning.tsx index 9db916a06e5d5..25e08cf076d17 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_clash_warning.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_clash_warning.tsx @@ -9,8 +9,8 @@ import React from 'react'; import { EuiButton, EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { UploadStatus, FileAnalysis } from '@kbn/file-upload-common'; -import { CLASH_TYPE } from '@kbn/file-upload-common/src/file_manager'; +import { CLASH_TYPE } from '@kbn/file-upload'; +import type { UploadStatus, FileAnalysis } from '@kbn/file-upload'; interface Props { uploadStatus: UploadStatus; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_picker.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_picker.tsx index c3be4700f690e..79d2250898786 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_picker.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_picker.tsx @@ -7,7 +7,7 @@ import type { EuiFilePickerProps } from '@elastic/eui'; import { EuiFormRow, EuiFilePicker, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import type { EuiFilePickerClass } from '@elastic/eui/src/components/form/file_picker/file_picker'; -import type { FileUploadManager } from '@kbn/file-upload-common'; +import type { FileUploadManager } from '@kbn/file-upload'; import { i18n } from '@kbn/i18n'; import type { FC } from 'react'; import React, { useCallback, useRef } from 'react'; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_status.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_status.tsx index 4a2689c5fd14e..82bd5f2045805 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_status.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_status.tsx @@ -21,8 +21,8 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { UploadStatus, FileAnalysis } from '@kbn/file-upload-common'; -import { STATUS, CLASH_TYPE } from '@kbn/file-upload-common/src/file_manager'; +import { type UploadStatus, type FileAnalysis, CLASH_TYPE } from '@kbn/file-upload'; +import { STATUS } from '@kbn/file-upload'; import { AnalysisSummary } from '../application/file_data_visualizer/components/analysis_summary'; import { FileContents } from '../application/file_data_visualizer/components/file_contents'; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite.tsx index f6c43d66aeeed..983eaafddcf05 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite.tsx @@ -8,13 +8,9 @@ import type { FC, PropsWithChildren } from 'react'; import React, { useMemo } from 'react'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; -import { - useFileUpload, - type OpenFileUploadLiteContext, - FileUploadContext, -} from '@kbn/file-upload-common'; +import { type OpenFileUploadLiteContext } from '@kbn/file-upload-common'; import type { CoreStart } from '@kbn/core/public'; -import { FileUploadManager } from '@kbn/file-upload-common/src/file_manager'; +import { FileUploadManager, useFileUpload, FileUploadContext } from '@kbn/file-upload'; import type { ResultLinks } from '../../common/app'; import { FileUploadLiteView } from './file_upload_lite_view'; import type { GetAdditionalLinks } from '../application/common/components/results_links'; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite_view.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite_view.tsx index 8de5b1de4f443..74501ea02b456 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite_view.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite_view.tsx @@ -22,9 +22,9 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import type { FC } from 'react'; import React, { useEffect } from 'react'; -import { useFileUploadContext } from '@kbn/file-upload-common'; import type { OpenFileUploadLiteContext } from '@kbn/file-upload-common/src/types'; -import { STATUS } from '@kbn/file-upload-common/src/file_manager'; +import { useFileUploadContext } from '@kbn/file-upload'; +import { STATUS } from '@kbn/file-upload'; import type { ResultLinks } from '../../common/app'; import type { GetAdditionalLinks } from '../application/common/components/results_links'; import { FileClashWarning } from './file_clash_warning'; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/import_errors.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/import_errors.tsx index edc4f0fee8a03..380c3a90de67c 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/import_errors.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/import_errors.tsx @@ -6,7 +6,7 @@ */ import { EuiCallOut, EuiSpacer } from '@elastic/eui'; -import type { UploadStatus } from '@kbn/file-upload-common'; +import type { UploadStatus } from '@kbn/file-upload'; import type { FC } from 'react'; import React from 'react'; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/index_input.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/index_input.tsx index 047b91ac369a3..f4213cd09fd0e 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/index_input.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/index_input.tsx @@ -11,7 +11,7 @@ import React, { useState } from 'react'; import useDebounce from 'react-use/lib/useDebounce'; import { i18n } from '@kbn/i18n'; import useMountedState from 'react-use/lib/useMountedState'; -import { STATUS } from '@kbn/file-upload-common/src/file_manager'; +import { STATUS } from '@kbn/file-upload'; import { useDataVisualizerKibana } from '../application/kibana_context'; interface Props { diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/overall_upload_progress.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/overall_upload_progress.tsx index ea347c80a0fcd..5c3e1bc5b1802 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/overall_upload_progress.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/overall_upload_progress.tsx @@ -5,7 +5,7 @@ * 2.0. */ import { EuiProgress } from '@elastic/eui'; -import type { FileAnalysis } from '@kbn/file-upload-common'; +import type { FileAnalysis } from '@kbn/file-upload'; import type { FC } from 'react'; import React from 'react'; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/overall_upload_status.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/overall_upload_status.tsx index aadb890ab0c2d..2b502ef59fcf4 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/overall_upload_status.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/overall_upload_status.tsx @@ -11,8 +11,8 @@ import type { EuiStepStatus } from '@elastic/eui'; import { EuiSpacer, EuiSteps } from '@elastic/eui'; import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; import { i18n } from '@kbn/i18n'; -import type { UploadStatus, FileAnalysis } from '@kbn/file-upload-common'; -import { STATUS } from '@kbn/file-upload-common/src/file_manager'; +import type { UploadStatus, FileAnalysis } from '@kbn/file-upload'; +import { STATUS } from '@kbn/file-upload'; import { FileStatus } from './file_status'; interface Props { diff --git a/x-pack/platform/plugins/private/data_visualizer/tsconfig.json b/x-pack/platform/plugins/private/data_visualizer/tsconfig.json index 051d5cabc6aae..924b7f026fc98 100644 --- a/x-pack/platform/plugins/private/data_visualizer/tsconfig.json +++ b/x-pack/platform/plugins/private/data_visualizer/tsconfig.json @@ -85,6 +85,7 @@ "@kbn/react-kibana-mount", "@kbn/core-ui-settings-browser", "@kbn/file-upload-common", + "@kbn/file-upload", "@kbn/charts-theme" ], "exclude": [ diff --git a/x-pack/platform/plugins/private/file_upload/common/constants.ts b/x-pack/platform/plugins/private/file_upload/common/constants.ts deleted file mode 100644 index 2ccf1315618c0..0000000000000 --- a/x-pack/platform/plugins/private/file_upload/common/constants.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export const UI_SETTING_MAX_FILE_SIZE = 'fileUpload:maxFileSize'; -export const MB = Math.pow(2, 20); -export const MAX_FILE_SIZE = '100MB'; -export const MAX_FILE_SIZE_BYTES = 524288000; // 500MB -export const ABSOLUTE_MAX_FILE_SIZE_BYTES = 1073741274; // 1GB -export const FILE_SIZE_DISPLAY_FORMAT = '0,0.[0] b'; -export const MAX_TIKA_FILE_SIZE_BYTES = 62914560; // 60MB - -// Value to use in the Elasticsearch index mapping meta data to identify the -// index as having been created by the ML File Data Visualizer. -export const INDEX_META_DATA_CREATED_BY = 'file-data-visualizer'; - -export const FILE_FORMATS = { - DELIMITED: 'delimited', - NDJSON: 'ndjson', - SEMI_STRUCTURED_TEXT: 'semi_structured_text', - TIKA: 'tika', -}; - -export const TIKA_PREVIEW_CHARS = 100000; - -// move all of these to package!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/x-pack/platform/plugins/private/file_upload/common/index.ts b/x-pack/platform/plugins/private/file_upload/common/index.ts index a331eb67e9786..eb5fcdc6b1c00 100644 --- a/x-pack/platform/plugins/private/file_upload/common/index.ts +++ b/x-pack/platform/plugins/private/file_upload/common/index.ts @@ -15,5 +15,3 @@ export type { InputOverrides, IngestPipeline, } from './types'; - -export { TIKA_PREVIEW_CHARS } from './constants'; diff --git a/x-pack/platform/plugins/private/file_upload/public/components/geo_upload_form/geo_file_picker.tsx b/x-pack/platform/plugins/private/file_upload/public/components/geo_upload_form/geo_file_picker.tsx index f813abb1510d7..a9b720428d208 100644 --- a/x-pack/platform/plugins/private/file_upload/public/components/geo_upload_form/geo_file_picker.tsx +++ b/x-pack/platform/plugins/private/file_upload/public/components/geo_upload_form/geo_file_picker.tsx @@ -8,7 +8,7 @@ import React, { Component } from 'react'; import { EuiFilePicker, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { MB } from '../../../common/constants'; +import { MB } from '@kbn/file-upload-common/src/constants'; import { GEO_FILE_TYPES, geoImporterFactory } from '../../importer/geo'; import type { GeoFileImporter, GeoFilePreview } from '../../importer/geo'; diff --git a/x-pack/platform/plugins/private/file_upload/public/importer/geo/abstract_geo_file_importer.tsx b/x-pack/platform/plugins/private/file_upload/public/importer/geo/abstract_geo_file_importer.tsx index b655ebab1e319..b63325b2516a7 100644 --- a/x-pack/platform/plugins/private/file_upload/public/importer/geo/abstract_geo_file_importer.tsx +++ b/x-pack/platform/plugins/private/file_upload/public/importer/geo/abstract_geo_file_importer.tsx @@ -9,10 +9,10 @@ import type { ReactNode } from 'react'; import type { Feature } from 'geojson'; import { i18n } from '@kbn/i18n'; import { ES_FIELD_TYPES } from '@kbn/data-plugin/public'; +import { MB } from '@kbn/file-upload-common/src/constants'; import type { GeoFileImporter, GeoFilePreview } from './types'; import type { CreateDocsResponse, ImportResults } from '../types'; import { Importer, IMPORT_RETRIES, MAX_CHUNK_CHAR_COUNT } from '../importer'; -import { MB } from '../../../common/constants'; import type { ImportDoc, ImportFailure, ImportResponse } from '../../../common/types'; import { geoJsonCleanAndValidate } from './geojson_clean_and_validate'; import { createChunks } from './create_chunks'; diff --git a/x-pack/platform/plugins/private/file_upload/public/importer/get_max_bytes.ts b/x-pack/platform/plugins/private/file_upload/public/importer/get_max_bytes.ts index d4f7457384533..edb8495ea7ac8 100644 --- a/x-pack/platform/plugins/private/file_upload/public/importer/get_max_bytes.ts +++ b/x-pack/platform/plugins/private/file_upload/public/importer/get_max_bytes.ts @@ -11,9 +11,10 @@ import { FILE_SIZE_DISPLAY_FORMAT, MAX_FILE_SIZE, MAX_FILE_SIZE_BYTES, - UI_SETTING_MAX_FILE_SIZE, MAX_TIKA_FILE_SIZE_BYTES, -} from '../../common/constants'; + UI_SETTING_MAX_FILE_SIZE, +} from '@kbn/file-upload-common/src/constants'; + import { getUiSettings } from '../kibana_services'; export function getMaxBytes() { diff --git a/x-pack/platform/plugins/private/file_upload/public/importer/importer.ts b/x-pack/platform/plugins/private/file_upload/public/importer/importer.ts index 19009d1814b50..5c78410c34059 100644 --- a/x-pack/platform/plugins/private/file_upload/public/importer/importer.ts +++ b/x-pack/platform/plugins/private/file_upload/public/importer/importer.ts @@ -14,8 +14,9 @@ import type { } from '@elastic/elasticsearch/lib/api/types'; import { i18n } from '@kbn/i18n'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { MB } from '@kbn/file-upload-common/src/constants'; import { getHttp } from '../kibana_services'; -import { MB } from '../../common/constants'; + import type { ImportDoc, ImportFailure, diff --git a/x-pack/platform/plugins/private/file_upload/public/importer/importer_factory.ts b/x-pack/platform/plugins/private/file_upload/public/importer/importer_factory.ts index fbe67cb702f7e..d516301431079 100644 --- a/x-pack/platform/plugins/private/file_upload/public/importer/importer_factory.ts +++ b/x-pack/platform/plugins/private/file_upload/public/importer/importer_factory.ts @@ -5,11 +5,11 @@ * 2.0. */ +import { FILE_FORMATS } from '@kbn/file-upload-common/src/constants'; import { MessageImporter } from './message_importer'; import { NdjsonImporter } from './ndjson_importer'; import { TikaImporter } from './tika_importer'; import type { ImportFactoryOptions } from './types'; -import { FILE_FORMATS } from '../../common/constants'; export function importerFactory(format: string, options: ImportFactoryOptions) { switch (format) { diff --git a/x-pack/platform/plugins/private/file_upload/server/import_data.ts b/x-pack/platform/plugins/private/file_upload/server/import_data.ts index 5ade5bf3e3489..bb1c31f3c3642 100644 --- a/x-pack/platform/plugins/private/file_upload/server/import_data.ts +++ b/x-pack/platform/plugins/private/file_upload/server/import_data.ts @@ -12,7 +12,8 @@ import type { IndicesIndexSettings, MappingTypeMapping, } from '@elastic/elasticsearch/lib/api/types'; -import { INDEX_META_DATA_CREATED_BY } from '../common/constants'; + +import { INDEX_META_DATA_CREATED_BY } from '@kbn/file-upload-common'; import type { ImportResponse, ImportFailure, diff --git a/x-pack/platform/plugins/private/file_upload/server/plugin.ts b/x-pack/platform/plugins/private/file_upload/server/plugin.ts index 3aa78862f3fa0..222527ed1ffa5 100644 --- a/x-pack/platform/plugins/private/file_upload/server/plugin.ts +++ b/x-pack/platform/plugins/private/file_upload/server/plugin.ts @@ -14,9 +14,9 @@ import type { PluginInitializerContext, } from '@kbn/core/server'; import { schema } from '@kbn/config-schema'; +import { UI_SETTING_MAX_FILE_SIZE, MAX_FILE_SIZE } from '@kbn/file-upload-common/src/constants'; import { fileUploadRoutes } from './routes'; import { initFileUploadTelemetry } from './telemetry'; -import { MAX_FILE_SIZE, UI_SETTING_MAX_FILE_SIZE } from '../common/constants'; import { setupCapabilities } from './capabilities'; import type { StartDeps, SetupDeps } from './types'; diff --git a/x-pack/platform/plugins/private/file_upload/server/preview_tika_contents.ts b/x-pack/platform/plugins/private/file_upload/server/preview_tika_contents.ts index c2f9f5a219243..dedc4614adae1 100644 --- a/x-pack/platform/plugins/private/file_upload/server/preview_tika_contents.ts +++ b/x-pack/platform/plugins/private/file_upload/server/preview_tika_contents.ts @@ -6,8 +6,8 @@ */ import type { IScopedClusterClient } from '@kbn/core/server'; +import { TIKA_PREVIEW_CHARS } from '@kbn/file-upload-common'; import type { PreviewTikaResponse } from '../common/types'; -import { TIKA_PREVIEW_CHARS } from '../common/constants'; /** * Returns the contents of a file using the attachment ingest processor diff --git a/x-pack/platform/plugins/private/file_upload/server/routes.ts b/x-pack/platform/plugins/private/file_upload/server/routes.ts index 8199adb8d1f43..0e64364cf39cd 100644 --- a/x-pack/platform/plugins/private/file_upload/server/routes.ts +++ b/x-pack/platform/plugins/private/file_upload/server/routes.ts @@ -7,7 +7,10 @@ import { schema } from '@kbn/config-schema'; import type { CoreSetup, Logger } from '@kbn/core/server'; -import { MAX_FILE_SIZE_BYTES, MAX_TIKA_FILE_SIZE_BYTES } from '../common/constants'; +import { + MAX_FILE_SIZE_BYTES, + MAX_TIKA_FILE_SIZE_BYTES, +} from '@kbn/file-upload-common/src/constants'; import { wrapError } from './error_wrapper'; import { importDataProvider } from './import_data'; import { getTimeFieldRange } from './get_time_field_range'; diff --git a/x-pack/platform/plugins/private/file_upload/tsconfig.json b/x-pack/platform/plugins/private/file_upload/tsconfig.json index ab6402f8d3501..cb8ad1050a7f3 100644 --- a/x-pack/platform/plugins/private/file_upload/tsconfig.json +++ b/x-pack/platform/plugins/private/file_upload/tsconfig.json @@ -16,6 +16,7 @@ "@kbn/config-schema", "@kbn/code-editor", "@kbn/datemath", + "@kbn/file-upload-common", ], "exclude": [ "target/**/*", diff --git a/x-pack/platform/plugins/private/translations/translations/fr-FR.json b/x-pack/platform/plugins/private/translations/translations/fr-FR.json index 1921e7a0ec437..5353393d16d33 100644 --- a/x-pack/platform/plugins/private/translations/translations/fr-FR.json +++ b/x-pack/platform/plugins/private/translations/translations/fr-FR.json @@ -5449,8 +5449,6 @@ "kbn-esql-validation-autocomplete.esql.autocomplete.examplesLabel": "Exemples :", "kbn-esql-validation-autocomplete.esql.autocomplete.fieldDefinition": "Champ spécifié par le tableau d'entrée", "kbn-esql-validation-autocomplete.esql.autocomplete.integrationDefinition": "Intégration", - "kbn-esql-validation-autocomplete.esql.autocomplete.specialIndexes.indexType.alias": "Alias", - "kbn-esql-validation-autocomplete.esql.autocomplete.specialIndexes.indexType.index": "Index", "kbn-esql-validation-autocomplete.esql.autocomplete.join.onKeyword": "Spécifier des conditions de champ JOIN", "kbn-esql-validation-autocomplete.esql.autocomplete.listDoc": "Liste d'éléments (…)", "kbn-esql-validation-autocomplete.esql.autocomplete.matchingFieldDefinition": "Utiliser pour correspondance avec {matchingField} de la politique", @@ -5461,6 +5459,8 @@ "kbn-esql-validation-autocomplete.esql.autocomplete.pipeDoc": "Barre verticale (|)", "kbn-esql-validation-autocomplete.esql.autocomplete.semiColonDoc": "Point-virgule (;)", "kbn-esql-validation-autocomplete.esql.autocomplete.sourceDefinition": "{type}", + "kbn-esql-validation-autocomplete.esql.autocomplete.specialIndexes.indexType.alias": "Alias", + "kbn-esql-validation-autocomplete.esql.autocomplete.specialIndexes.indexType.index": "Index", "kbn-esql-validation-autocomplete.esql.autocomplete.techPreviewLabel": "Version d'évaluation technique", "kbn-esql-validation-autocomplete.esql.autocomplete.timeSystemParamEnd": "L'heure de fin à partir du sélecteur de date", "kbn-esql-validation-autocomplete.esql.autocomplete.timeSystemParamStart": "L'heure de début à partir du sélecteur de date", @@ -15932,11 +15932,6 @@ "xpack.dataVisualizer.file.fileErrorCallouts.findFileStructurePermissionDenied.title": "Autorisation refusée", "xpack.dataVisualizer.file.fileErrorCallouts.overrideButton": "Appliquer le remplacement des paramètres", "xpack.dataVisualizer.file.fileErrorCallouts.revertingToPreviousSettingsDescription": "Retour aux paramètres précédents", - "xpack.dataVisualizer.file.fileManager.errorCreatingDataView": "Erreur lors de la création de la vue de données", - "xpack.dataVisualizer.file.fileManager.errorDeletingPipelines": "Erreur lors de la suppression de pipelines", - "xpack.dataVisualizer.file.fileManager.errorDeployingModel": "Erreur lors du déploiement du modèle", - "xpack.dataVisualizer.file.fileManager.errorImportingData": "Erreur lors de l'importation de données", - "xpack.dataVisualizer.file.fileManager.errorInitializing": "Erreur lors de l'initialisation du pipeline d'index et d'ingestion", "xpack.dataVisualizer.file.filePicker.selectOrDragAndDropFiles": "Sélectionner ou glisser-déposer les fichiers", "xpack.dataVisualizer.file.fileStatus.deleteFile": "Supprimer le fichier", "xpack.dataVisualizer.file.fileStatus.fileFormatClash": "Format du fichier différent de celui des autres fichiers", @@ -35218,27 +35213,28 @@ "xpack.securitySolution.assetInventory.dataTable.fieldsModalClose": "Fermer", "xpack.securitySolution.assetInventory.dataTable.fieldsModalReset": "Réinitialiser les champs", "xpack.securitySolution.assetInventory.dataTable.fieldsModalTitle": "Champs", + "xpack.securitySolution.assetInventory.emptyState.description": "Essayez de modifier votre recherche ou votre ensemble de filtres", + "xpack.securitySolution.assetInventory.emptyState.illustrationAlt": "Aucun résultat", + "xpack.securitySolution.assetInventory.emptyState.readDocsLink": "Lisez les documents", + "xpack.securitySolution.assetInventory.emptyState.resetFiltersButton": "Réinitialiser les filtres", + "xpack.securitySolution.assetInventory.emptyState.title": "Aucun résultat ne correspond à vos critères de recherche.", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.agentlessAWSCredentialsForm.cloudFormation.launchButton": "Lancer CloudFormation", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.description": "Les clés d'accès sont des informations d'identification à long terme pour un utilisateur IAM ou l'utilisateur racine du compte AWS. Utilisez AWS CloudFormation (un outil AWS intégré) ou une série d'étapes manuelles pour configurer l'accès. {learnMore}.", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.learnMoreLinkText": "En savoir plus sur CloudFormation", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.steps.launch": "Cliquez sur le bouton {launchCloudFormation} ci-dessous.", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.accept": "Cochez la case située sous les {capabilities} dans le formulaire d'évaluation CloudFormation : {acknowledge}", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.accept.acknowledge": "Je reconnais qu'AWS CloudFormation peut créer des ressources IAM.", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.accept.capabilties": "fonctionnalités", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.create": "Cliquez sur {createStack}.", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.credentials": "Copiez les {accessKeyId} et les {secretAccessKey}, puis collez les informations d'identification ci-dessous", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.region": "(Facultatif) Modifiez la {amazonRegion} dans le coin supérieur droit pour qu'elle corresponde à la région où vous voulez déployer la suite", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.stackStatus": "Une fois que l'état de la suite est {createComplete}, cliquez sur l'onglet Sorties", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.googleCloudShell.cloudCredentials.button": "Lancer Google Cloud Shell", + "xpack.securitySolution.assetInventory.fleetIntegration.assetIntegration.integration.fieldRequired": "{field} est obligatoire", "xpack.securitySolution.assetInventory.fleetIntegration.awsAccountType.awsOrganizationDescription": "Connectez Elastic à chaque compte AWS (actuel et futur) de votre environnement en fournissant à Elastic un accès en lecture seule (configuration) à votre organisation AWS.", "xpack.securitySolution.assetInventory.fleetIntegration.awsAccountType.awsOrganizationLabel": "AWS Organizations", "xpack.securitySolution.assetInventory.fleetIntegration.awsAccountType.singleAccountLabel": "Compte unique", "xpack.securitySolution.assetInventory.fleetIntegration.awsAccountTypeDescriptionLabel": "Sélectionnez un compte unique ou une organisation, puis saisissez un nom et une description pour permettre l'identification de cette intégration.", - "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.azureOrganizationDescription": "Connectez Elastic à chaque abonnement Azure (actuel et futur) de votre environnement en fournissant à Elastic un accès en lecture seule (configuration) à votre organisation Azure (groupe racine du locataire).", - "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.azureOrganizationLabel": "Organisation Azure", - "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.singleAccountDescription": "Un déploiement vers un seul abonnement suffit pour une démonstration de faisabilité initiale. Pour garantir une couverture complète, il est fortement recommandé de déployer la gestion du niveau de sécurité du cloud au niveau de l'organisation (groupe racine du locataire), ce qui connecte automatiquement tous les abonnements (actuels et futurs).", - "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.singleAccountLabel": "Abonnement unique", - "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountTypeDescriptionLabel": "Choisissez d'intégrer une organisation Azure (groupe racine du locataire) ou un seul abonnement Azure, puis saisissez un nom et une description pour permettre l'identification de cette intégration.", - "xpack.securitySolution.assetInventory.fleetIntegration.configureAssetIntegrationDescription": "Sélectionner le fournisseur de services cloud (CSP) que vous souhaitez monitorer, puis remplir le nom et la description pour faciliter l'identification de cette intégration", - "xpack.securitySolution.assetInventory.fleetIntegration.editWarning.calloutTitle": "Modification des détails de l'intégration", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpOrganizationDescription": "Connectez Elastic à chaque projet Google Cloud (actuel et futur) de votre environnement en fournissant à Elastic un accès en lecture seule (configuration) à votre organisation Google Cloud", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpOrganizationLabel": "Organisation Google Cloud", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpSingleAccountLabel": "Projet unique", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountTypeDescriptionLabel": "Sélectionnez un projet unique ou une organisation, puis saisissez un nom et une description pour permettre l'identification de cette intégration.", - "xpack.securitySolution.assetInventory.fleetIntegration.integrationDescriptionLabel": "Description", - "xpack.securitySolution.assetInventory.fleetIntegration.integrationNameLabel": "Nom", - "xpack.securitySolution.assetInventory.fleetIntegration.integrationSettingsTitle": "Paramètres de l'intégration", - "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentBasedRadioDescription": "Déployez un agent Elastic dans votre environnement de cloud", - "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentBasedRadioLabel": "Basée sur un agent", - "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentlessRadioLabel": "Sans agent", "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.cloudFormationSetupNote": "Pour en savoir plus, consultez la {documentation}", "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.cloudFormationSetupStep.hostRequirement": "Assurez-vous que \"New hosts\" (Nouveaux hôtes) est sélectionné dans la section \"Where to add this integration?\" (Où ajouter cette intégration ?) ci-dessous", "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.cloudFormationSetupStep.launch": "Dans la boîte de dialogue qui apparaît, cliquez sur le bouton \"Lancer CloudFormation\".", @@ -35248,6 +35244,11 @@ "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.cloudFormationSetupStep.save": "Cliquez sur le bouton \"Enregistrer et continuer\" en bas à droite de cette page", "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.setupFormatOptions.manual": "Manuel", "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.setupInfoContentTitle": "Configurer l'accès", + "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.azureOrganizationDescription": "Connectez Elastic à chaque abonnement Azure (actuel et futur) de votre environnement en fournissant à Elastic un accès en lecture seule (configuration) à votre organisation Azure (groupe racine du locataire).", + "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.azureOrganizationLabel": "Organisation Azure", + "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.singleAccountDescription": "Un déploiement vers un seul abonnement suffit pour une démonstration de faisabilité initiale. Pour garantir une couverture complète, il est fortement recommandé de déployer la gestion du niveau de sécurité du cloud au niveau de l'organisation (groupe racine du locataire), ce qui connecte automatiquement tous les abonnements (actuels et futurs).", + "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.singleAccountLabel": "Abonnement unique", + "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountTypeDescriptionLabel": "Choisissez d'intégrer une organisation Azure (groupe racine du locataire) ou un seul abonnement Azure, puis saisissez un nom et une description pour permettre l'identification de cette intégration.", "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.armTemplateSetupNote": "Pour en savoir plus, consultez la {documentation}", "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.armTemplateSetupStep.hostRequirement": "Assurez-vous que \"New hosts\" (Nouveaux hôtes) est sélectionné dans la section \"Where to add this integration?\" (Où ajouter cette intégration ?) ci-dessous", "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.armTemplateSetupStep.launch": "Dans la boîte de dialogue qui apparaît, copiez la commande Bash appropriée, puis cliquez sur le bouton \"Lancer le modèle ARM\".", @@ -35272,24 +35273,20 @@ "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.setupFormatOptions.manual": "Manuel", "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.setupInfoContentTitle": "Configurer l'accès", "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.tenantIdLabel": "ID locataire", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.agentlessAWSCredentialsForm.cloudFormation.launchButton": "Lancer CloudFormation", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.description": "Les clés d'accès sont des informations d'identification à long terme pour un utilisateur IAM ou l'utilisateur racine du compte AWS. Utilisez AWS CloudFormation (un outil AWS intégré) ou une série d'étapes manuelles pour configurer l'accès. {learnMore}.", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.learnMoreLinkText": "En savoir plus sur CloudFormation", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.steps.launch": "Cliquez sur le bouton {launchCloudFormation} ci-dessous.", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.accept": "Cochez la case située sous les {capabilities} dans le formulaire d'évaluation CloudFormation : {acknowledge}", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.accept.acknowledge": "Je reconnais qu'AWS CloudFormation peut créer des ressources IAM.", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.accept.capabilties": "fonctionnalités", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.create": "Cliquez sur {createStack}.", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.credentials": "Copiez les {accessKeyId} et les {secretAccessKey}, puis collez les informations d'identification ci-dessous", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.region": "(Facultatif) Modifiez la {amazonRegion} dans le coin supérieur droit pour qu'elle corresponde à la région où vous voulez déployer la suite", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.stackStatus": "Une fois que l'état de la suite est {createComplete}, cliquez sur l'onglet Sorties", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.googleCloudShell.cloudCredentials.button": "Lancer Google Cloud Shell", - "xpack.securitySolution.assetInventory.fleetIntegration.assetIntegration.integration.fieldRequired": "{field} est obligatoire", + "xpack.securitySolution.assetInventory.fleetIntegration.configureAssetIntegrationDescription": "Sélectionner le fournisseur de services cloud (CSP) que vous souhaitez monitorer, puis remplir le nom et la description pour faciliter l'identification de cette intégration", + "xpack.securitySolution.assetInventory.fleetIntegration.editWarning.calloutTitle": "Modification des détails de l'intégration", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpOrganizationDescription": "Connectez Elastic à chaque projet Google Cloud (actuel et futur) de votre environnement en fournissant à Elastic un accès en lecture seule (configuration) à votre organisation Google Cloud", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpOrganizationLabel": "Organisation Google Cloud", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpSingleAccountLabel": "Projet unique", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountTypeDescriptionLabel": "Sélectionnez un projet unique ou une organisation, puis saisissez un nom et une description pour permettre l'identification de cette intégration.", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.cloudShellSetupStep.hostRequirement": "Assurez-vous que \"New hosts\" (Nouveaux hôtes) est sélectionné dans la section \"Where to add this integration?\" (Où ajouter cette intégration ?) ci-dessous", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.cloudShellSetupStep.launch": "Cliquez sur \"Save and Continue\" (Sauvegarder et continuer) en bas à droite de la page. Puis cliquez sur \"Launch Google Cloud Shell\" (Lancer Google Cloud Shell) dans la boîte de dialogue qui apparaît", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.cloudShellSetupStep.login": "Connectez-vous à votre console Google Cloud", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.cloudShellSetupStep.save": "Notez l'ID de projet Google Cloud du projet que vous souhaitez monitorer", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.credentialsFileOption": "Fichier d'informations d'identification", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialFileText": "Chemin vers le fichier JSON qui contient les informations d'identification et la clé utilisés pour souscrire", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialJSONText": "Blob JSON qui contient les informations d'identification et la clé utilisées pour souscrire", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialSelectBoxTitle": "Informations d'identification", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.organizationCloudShellSetupStep.save": "Notez l'ID d'organisation Google Cloud de l'organisation que vous souhaitez monitorer et l'ID de projet auquel vous voulez fournir des ressources à des fins de monitoring et indiquez-les dans les zones de saisie ci-dessous", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.organizationIdFieldLabel": "ID d'organisation", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.projectidFieldLabel": "ID de projet", @@ -35305,15 +35302,13 @@ "xpack.securitySolution.assetInventory.fleetIntegration.googleCloudShellCredentials.guide.steps.copyWithProjectId": "Remplacez dans la commande suivante par l'ID de votre projet, puis copiez la commande", "xpack.securitySolution.assetInventory.fleetIntegration.googleCloudShellCredentials.guide.steps.launch": "Connectez-vous à votre {googleCloudConsole}", "xpack.securitySolution.assetInventory.fleetIntegration.googleCloudShellCredentials.guide.steps.runCloudShellScript": "Collez et exécutez la commande dans le terminal {googleCloudShell}.", + "xpack.securitySolution.assetInventory.fleetIntegration.integrationDescriptionLabel": "Description", + "xpack.securitySolution.assetInventory.fleetIntegration.integrationNameLabel": "Nom", + "xpack.securitySolution.assetInventory.fleetIntegration.integrationSettingsTitle": "Paramètres de l'intégration", + "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentBasedRadioDescription": "Déployez un agent Elastic dans votre environnement de cloud", + "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentBasedRadioLabel": "Basée sur un agent", + "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentlessRadioLabel": "Sans agent", "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnologySelector.deploymentOptionsTitle": "Options de déploiement", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialFileText": "Chemin vers le fichier JSON qui contient les informations d'identification et la clé utilisés pour souscrire", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialJSONText": "Blob JSON qui contient les informations d'identification et la clé utilisées pour souscrire", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialSelectBoxTitle": "Informations d'identification", - "xpack.securitySolution.assetInventory.emptyState.description": "Essayez de modifier votre recherche ou votre ensemble de filtres", - "xpack.securitySolution.assetInventory.emptyState.illustrationAlt": "Aucun résultat", - "xpack.securitySolution.assetInventory.emptyState.readDocsLink": "Lisez les documents", - "xpack.securitySolution.assetInventory.emptyState.resetFiltersButton": "Réinitialiser les filtres", - "xpack.securitySolution.assetInventory.emptyState.title": "Aucun résultat ne correspond à vos critères de recherche.", "xpack.securitySolution.assetInventory.searchBar.searchPlaceholder": "Filtrez vos données en utilisant la syntaxe KQL", "xpack.securitySolution.assetInventory.technicalPreviewLabel": "Version d'évaluation technique", "xpack.securitySolution.assetInventory.technicalPreviewTooltip": "Cette fonctionnalité est expérimentale et n’est pas prise en charge. Elle peut changer ou être supprimée à tout moment.", diff --git a/x-pack/platform/plugins/private/translations/translations/ja-JP.json b/x-pack/platform/plugins/private/translations/translations/ja-JP.json index c8669c6919b1c..664db4b0a150f 100644 --- a/x-pack/platform/plugins/private/translations/translations/ja-JP.json +++ b/x-pack/platform/plugins/private/translations/translations/ja-JP.json @@ -5443,8 +5443,6 @@ "kbn-esql-validation-autocomplete.esql.autocomplete.examplesLabel": "例:", "kbn-esql-validation-autocomplete.esql.autocomplete.fieldDefinition": "入力テーブルで指定されたフィールド", "kbn-esql-validation-autocomplete.esql.autocomplete.integrationDefinition": "統合", - "kbn-esql-validation-autocomplete.esql.autocomplete.specialIndexes.indexType.alias": "Alias", - "kbn-esql-validation-autocomplete.esql.autocomplete.specialIndexes.indexType.index": "インデックス", "kbn-esql-validation-autocomplete.esql.autocomplete.join.onKeyword": "JOINフィールド条件を指定", "kbn-esql-validation-autocomplete.esql.autocomplete.listDoc": "項目のリスト(...)", "kbn-esql-validation-autocomplete.esql.autocomplete.matchingFieldDefinition": "ポリシーの{matchingField}で照合するために使用", @@ -5456,6 +5454,8 @@ "kbn-esql-validation-autocomplete.esql.autocomplete.policyDefinition": "{count, plural, other {インデックス}}で定義されたポリシー:{indices}", "kbn-esql-validation-autocomplete.esql.autocomplete.semiColonDoc": "セミコロン(;)", "kbn-esql-validation-autocomplete.esql.autocomplete.sourceDefinition": "{type}", + "kbn-esql-validation-autocomplete.esql.autocomplete.specialIndexes.indexType.alias": "Alias", + "kbn-esql-validation-autocomplete.esql.autocomplete.specialIndexes.indexType.index": "インデックス", "kbn-esql-validation-autocomplete.esql.autocomplete.techPreviewLabel": "テクニカルプレビュー", "kbn-esql-validation-autocomplete.esql.autocomplete.timeSystemParamEnd": "日付ピッカーの終了日時", "kbn-esql-validation-autocomplete.esql.autocomplete.timeSystemParamStart": "日付ピッカーの開始日時", @@ -15913,11 +15913,6 @@ "xpack.dataVisualizer.file.fileErrorCallouts.findFileStructurePermissionDenied.title": "パーミッションが拒否されました", "xpack.dataVisualizer.file.fileErrorCallouts.overrideButton": "上書き設定を適用", "xpack.dataVisualizer.file.fileErrorCallouts.revertingToPreviousSettingsDescription": "以前の設定に戻しています。", - "xpack.dataVisualizer.file.fileManager.errorCreatingDataView": "データビューの作成エラー", - "xpack.dataVisualizer.file.fileManager.errorDeletingPipelines": "パイプラインの削除エラー", - "xpack.dataVisualizer.file.fileManager.errorDeployingModel": "モデルのデプロイエラー", - "xpack.dataVisualizer.file.fileManager.errorImportingData": "データのインポートエラー", - "xpack.dataVisualizer.file.fileManager.errorInitializing": "インデックスおよびインジェストパイプラインの初期化エラー", "xpack.dataVisualizer.file.filePicker.selectOrDragAndDropFiles": "ファイルを選択するかドラッグ & ドロップしてください", "xpack.dataVisualizer.file.fileStatus.deleteFile": "ファイルを削除", "xpack.dataVisualizer.file.fileStatus.fileFormatClash": "他のファイルとは異なるファイル形式", @@ -35198,27 +35193,20 @@ "xpack.securitySolution.assetInventory.emptyState.illustrationAlt": "0件ヒット", "xpack.securitySolution.assetInventory.emptyState.resetFiltersButton": "フィルターをリセット", "xpack.securitySolution.assetInventory.emptyState.title": "検索条件と一致する結果がありません。", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.agentlessAWSCredentialsForm.cloudFormation.launchButton": "CloudFormationを起動", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.description": "アクセスキーはIAMユーザーまたはAWSアカウントルートユーザー用の長期的な資格情報です。AWS CloudFormation(AWSに組み込まれたツール)または一連の手動ステップを利用してアクセスを設定します。{learnMore}。", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.learnMoreLinkText": "CloudFormationの詳細", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.steps.launch": "下の[{launchCloudFormation}]ボタンをクリックします。", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.accept.acknowledge": "AWS CloudFormationがIAMリソースを作成する可能性があることを承諾します。", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.accept.capabilties": "機能", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.create": "{createStack}をクリックします。", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.credentials": "{accessKeyId}と{secretAccessKey}をコピーして、以下に資格情報を貼り付けます", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.stackStatus": "スタックステータスが{createComplete}になったら、[出力]タブをクリックします", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.googleCloudShell.cloudCredentials.button": "Google Cloud Shellを起動", "xpack.securitySolution.assetInventory.fleetIntegration.awsAccountType.awsOrganizationDescription": "お客様のAWS組織への読み取り専用(設定)アクセスをElasticに提供することで、お客様の環境にあるすべてのAWSアカウント(現在および将来)にElasticを接続します。", "xpack.securitySolution.assetInventory.fleetIntegration.awsAccountType.awsOrganizationLabel": "AWS組織", "xpack.securitySolution.assetInventory.fleetIntegration.awsAccountType.singleAccountLabel": "1つのアカウント", "xpack.securitySolution.assetInventory.fleetIntegration.awsAccountTypeDescriptionLabel": "1つのアカウントまたは組織のいずれかを選択し、この統合を識別するための名前と説明を入力します。", - "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.azureOrganizationDescription": "お客様のAzure組織(テナントルートグループ)への読み取り専用(設定)アクセスをElasticに提供することで、お客様の環境にあるすべてのAzureサブスクリプション(現在および将来)にElasticを接続します。", - "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.azureOrganizationLabel": "Azure組織", - "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.singleAccountDescription": "1つのサブスクリプションへのデプロイは、最初のPOCに適しています。完全に対応するためには、組織(テナントルートグループ)レベルでCSPMをデプロイし、すべてのサブスクリプション(現在と将来の両方)を自動的に接続することを強くお勧めします。", - "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.singleAccountLabel": "1つのサブスクリプション", - "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountTypeDescriptionLabel": "Azure組織(テナントルートグループ)または単一のAzureサブスクリプションのオンボーディングのいずれかを選択し、この統合を識別するのに役立つ名前と説明を記入します。", - "xpack.securitySolution.assetInventory.fleetIntegration.configureAssetIntegrationDescription": "監視するクラウドサービスプロバイダー(CSP)を選択し、この統合を識別するのに役立つ名前と説明を記入します", - "xpack.securitySolution.assetInventory.fleetIntegration.editWarning.calloutTitle": "統合詳細の変更中", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpOrganizationDescription": "お客様のGCP組織の読み取り専用(設定)アクセスをElasticに提供することで、お客様の環境にあるすべてのGCPプロジェクト(現在および将来)にElasticを接続します。", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpOrganizationLabel": "GCP組織", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpSingleAccountLabel": "1つのプロジェクト", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountTypeDescriptionLabel": "1つのプロジェクトまたは組織のいずれかを選択し、この統合を識別するための名前と説明を入力します。", - "xpack.securitySolution.assetInventory.fleetIntegration.integrationDescriptionLabel": "説明", - "xpack.securitySolution.assetInventory.fleetIntegration.integrationNameLabel": "名前", - "xpack.securitySolution.assetInventory.fleetIntegration.integrationSettingsTitle": "統合設定", - "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentBasedRadioDescription": "Elasticエージェントをクラウド環境にデプロイ", - "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentBasedRadioLabel": "エージェントベース", - "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentlessRadioLabel": "エージェントレス", "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.cloudFormationSetupNote": "詳細に関しては、{documentation}を参照してください。", "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.cloudFormationSetupStep.hostRequirement": "以下の\"この統合を追加する場所\"セクションで、\"新しいホスト\"が選択されていることを確認します", "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.cloudFormationSetupStep.launch": "続くポップアップモーダルで、\"CloudFormationを起動\"ボタンをクリックします。", @@ -35228,6 +35216,11 @@ "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.cloudFormationSetupStep.save": "このページの右下にある\"保存して続行\"ボタンをクリックしてください", "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.setupFormatOptions.manual": "手動", "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.setupInfoContentTitle": "アクセスの設定", + "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.azureOrganizationDescription": "お客様のAzure組織(テナントルートグループ)への読み取り専用(設定)アクセスをElasticに提供することで、お客様の環境にあるすべてのAzureサブスクリプション(現在および将来)にElasticを接続します。", + "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.azureOrganizationLabel": "Azure組織", + "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.singleAccountDescription": "1つのサブスクリプションへのデプロイは、最初のPOCに適しています。完全に対応するためには、組織(テナントルートグループ)レベルでCSPMをデプロイし、すべてのサブスクリプション(現在と将来の両方)を自動的に接続することを強くお勧めします。", + "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.singleAccountLabel": "1つのサブスクリプション", + "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountTypeDescriptionLabel": "Azure組織(テナントルートグループ)または単一のAzureサブスクリプションのオンボーディングのいずれかを選択し、この統合を識別するのに役立つ名前と説明を記入します。", "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.armTemplateSetupNote": "詳細に関しては、{documentation}を参照してください。", "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.armTemplateSetupStep.hostRequirement": "以下の\"この統合を追加する場所\"セクションで、\"新しいホスト\"が選択されていることを確認します", "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.armTemplateSetupStep.launch": "続いて表示されるポップアップモーダルで、関連するBashコマンドをコピーし、\"ARMテンプレート\"ボタンをクリックします。", @@ -35252,32 +35245,34 @@ "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.setupFormatOptions.manual": "手動", "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.setupInfoContentTitle": "アクセスの設定", "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.tenantIdLabel": "テナントID", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.agentlessAWSCredentialsForm.cloudFormation.launchButton": "CloudFormationを起動", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.description": "アクセスキーはIAMユーザーまたはAWSアカウントルートユーザー用の長期的な資格情報です。AWS CloudFormation(AWSに組み込まれたツール)または一連の手動ステップを利用してアクセスを設定します。{learnMore}。", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.learnMoreLinkText": "CloudFormationの詳細", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.steps.launch": "下の[{launchCloudFormation}]ボタンをクリックします。", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.accept.acknowledge": "AWS CloudFormationがIAMリソースを作成する可能性があることを承諾します。", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.accept.capabilties": "機能", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.create": "{createStack}をクリックします。", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.credentials": "{accessKeyId}と{secretAccessKey}をコピーして、以下に資格情報を貼り付けます", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.stackStatus": "スタックステータスが{createComplete}になったら、[出力]タブをクリックします", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.googleCloudShell.cloudCredentials.button": "Google Cloud Shellを起動", + "xpack.securitySolution.assetInventory.fleetIntegration.configureAssetIntegrationDescription": "監視するクラウドサービスプロバイダー(CSP)を選択し、この統合を識別するのに役立つ名前と説明を記入します", + "xpack.securitySolution.assetInventory.fleetIntegration.editWarning.calloutTitle": "統合詳細の変更中", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpOrganizationDescription": "お客様のGCP組織の読み取り専用(設定)アクセスをElasticに提供することで、お客様の環境にあるすべてのGCPプロジェクト(現在および将来)にElasticを接続します。", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpOrganizationLabel": "GCP組織", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpSingleAccountLabel": "1つのプロジェクト", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountTypeDescriptionLabel": "1つのプロジェクトまたは組織のいずれかを選択し、この統合を識別するための名前と説明を入力します。", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.cloudShellSetupStep.hostRequirement": "以下の\"この統合を追加する場所\"セクションで、\"新しいホスト\"が選択されていることを確認します", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.cloudShellSetupStep.launch": "ページ右下の\"保存して続行\"をクリックします。次に、ポップアップモーダルで\"Google Cloud Shellを起動\"をクリックします。", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.cloudShellSetupStep.login": "Google Cloud Consoleにログイン", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.cloudShellSetupStep.save": "監視するプロジェクトのGCPプロジェクトIDをメモします", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.credentialsFileOption": "資格情報ファイル", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.credentialsJsonOption": "資格情報JSON", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialFileText": "サブスクライブに使用される資格情報とキーを含むJSONファイルへのパス", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialJSONText": "サブスクライブに使用される資格情報とキーを含むJSON blob", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialSelectBoxTitle": "資格情報", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.organizationCloudShellSetupStep.save": "監視する組織のGCP組織IDと、監視の目的でリソースをプロビジョニングするプロジェクトIDをメモし、以下の入力ボックスに入力します", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.organizationIdFieldLabel": "組織 ID", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.projectidFieldLabel": "プロジェクト ID", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.setupFormatOptions.googleCloudShell": "Google Cloud Shell", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.setupFormatOptions.manual": "手動", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.setupInfoContentTitle": "アクセスの設定", + "xpack.securitySolution.assetInventory.fleetIntegration.integrationDescriptionLabel": "説明", + "xpack.securitySolution.assetInventory.fleetIntegration.integrationNameLabel": "名前", + "xpack.securitySolution.assetInventory.fleetIntegration.integrationSettingsTitle": "統合設定", + "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentBasedRadioDescription": "Elasticエージェントをクラウド環境にデプロイ", + "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentBasedRadioLabel": "エージェントベース", + "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentlessRadioLabel": "エージェントレス", "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnologySelector.deploymentOptionsTitle": "デプロイオプション", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialFileText": "サブスクライブに使用される資格情報とキーを含むJSONファイルへのパス", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialJSONText": "サブスクライブに使用される資格情報とキーを含むJSON blob", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialSelectBoxTitle": "資格情報", "xpack.securitySolution.assetInventory.searchBar.searchPlaceholder": "KQL構文を使用してデータをフィルタリング", "xpack.securitySolution.assetInventory.technicalPreviewLabel": "テクニカルプレビュー", "xpack.securitySolution.assetInventory.technicalPreviewTooltip": "この機能は実験的であり、サポートされていません。どこかの時点で変更または削除される場合があります。", diff --git a/x-pack/platform/plugins/private/translations/translations/zh-CN.json b/x-pack/platform/plugins/private/translations/translations/zh-CN.json index 355c180b80d24..2d1ba2d509645 100644 --- a/x-pack/platform/plugins/private/translations/translations/zh-CN.json +++ b/x-pack/platform/plugins/private/translations/translations/zh-CN.json @@ -5451,8 +5451,6 @@ "kbn-esql-validation-autocomplete.esql.autocomplete.examplesLabel": "示例:", "kbn-esql-validation-autocomplete.esql.autocomplete.fieldDefinition": "由输入表指定的字段", "kbn-esql-validation-autocomplete.esql.autocomplete.integrationDefinition": "集成", - "kbn-esql-validation-autocomplete.esql.autocomplete.specialIndexes.indexType.alias": "别名", - "kbn-esql-validation-autocomplete.esql.autocomplete.specialIndexes.indexType.index": "索引", "kbn-esql-validation-autocomplete.esql.autocomplete.join.onKeyword": "指定 JOIN 字段条件", "kbn-esql-validation-autocomplete.esql.autocomplete.listDoc": "项目列表 ( ...)", "kbn-esql-validation-autocomplete.esql.autocomplete.matchingFieldDefinition": "用于匹配策略上的 {matchingField}", @@ -5464,6 +5462,8 @@ "kbn-esql-validation-autocomplete.esql.autocomplete.policyDefinition": "策略在{count, plural, other {索引}}上定义:{indices}", "kbn-esql-validation-autocomplete.esql.autocomplete.semiColonDoc": "分号 (;)", "kbn-esql-validation-autocomplete.esql.autocomplete.sourceDefinition": "{type}", + "kbn-esql-validation-autocomplete.esql.autocomplete.specialIndexes.indexType.alias": "别名", + "kbn-esql-validation-autocomplete.esql.autocomplete.specialIndexes.indexType.index": "索引", "kbn-esql-validation-autocomplete.esql.autocomplete.techPreviewLabel": "技术预览", "kbn-esql-validation-autocomplete.esql.autocomplete.timeSystemParamEnd": "日期选取器中的结束时间", "kbn-esql-validation-autocomplete.esql.autocomplete.timeSystemParamStart": "日期选取器中的开始时间", @@ -15948,11 +15948,6 @@ "xpack.dataVisualizer.file.fileErrorCallouts.findFileStructurePermissionDenied.title": "权限被拒绝", "xpack.dataVisualizer.file.fileErrorCallouts.overrideButton": "应用覆盖设置", "xpack.dataVisualizer.file.fileErrorCallouts.revertingToPreviousSettingsDescription": "恢复到以前的设置", - "xpack.dataVisualizer.file.fileManager.errorCreatingDataView": "创建数据视图时出错", - "xpack.dataVisualizer.file.fileManager.errorDeletingPipelines": "删除管道时出错", - "xpack.dataVisualizer.file.fileManager.errorDeployingModel": "部署模型时出错", - "xpack.dataVisualizer.file.fileManager.errorImportingData": "导入数据时出错", - "xpack.dataVisualizer.file.fileManager.errorInitializing": "初始化索引和采集管道时出错", "xpack.dataVisualizer.file.filePicker.selectOrDragAndDropFiles": "选择或拖放文件", "xpack.dataVisualizer.file.fileStatus.deleteFile": "移除文件", "xpack.dataVisualizer.file.fileStatus.fileFormatClash": "文件格式与其他文件不同", @@ -35257,44 +35252,30 @@ "xpack.securitySolution.assetInventory.emptyState.readDocsLink": "阅读文档", "xpack.securitySolution.assetInventory.emptyState.resetFiltersButton": "重置筛选", "xpack.securitySolution.assetInventory.emptyState.title": "没有任何结果匹配您的搜索条件", + "xpack.securitySolution.assetInventory.fleetIntegration.accessKeyIdLabel": "访问密钥 ID", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.agentlessAWSCredentialsForm.cloudFormation.launchButton": "启动 CloudFormation", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.description": "访问密钥是 IAM 用户或 AWS 帐户根用户的长期凭据。利用 AWS CloudFormation(内置 AWS 工具)或一系列手动步骤设置访问权限。{learnMore}。", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.learnMoreLinkText": "详细了解 CloudFormation", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.steps.launch": "单击下面的 {launchCloudFormation} 按钮。", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.steps.organizationLogin": "作为 {admin} 登录到您要载入的 AWS 组织管理帐户", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.steps.singleLogin": "作为 {admin} 登录到您要载入的 AWS 帐户", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.accept": "在打开的 CloudFormation 堆栈复查表单中,勾选 {capabilities} 下方的复选框:{acknowledge}", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.accept.acknowledge": "我确认,AWS CloudFormation 可以创建 IAM 资源。", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.accept.capabilties": "功能", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.create": "单击 {createStack}。", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.credentials": "复制 {accessKeyId} 和 {secretAccessKey},然后粘贴以下凭据", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.region": "(可选)将右上角的 {amazonRegion} 更改为要将您的堆栈部署到的区域", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.stackStatus": "一旦堆栈状态为 {createComplete},则单击“输出”选项卡", + "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.googleCloudShell.cloudCredentials.button": "启动 Google Cloud Shell", + "xpack.securitySolution.assetInventory.fleetIntegration.assetIntegration.integration.fieldRequired": "{field} 必填", + "xpack.securitySolution.assetInventory.fleetIntegration.assumeRoleDescription": "IAM 角色 Amazon 资源名称 (ARN) 是您可在 AWS 帐户中创建的 IAM 身份。创建 IAM 角色时,用户可以定义该角色的权限。角色没有标准的长期凭据,如密码或访问密钥。", + "xpack.securitySolution.assetInventory.fleetIntegration.assumeRoleLabel": "接管角色", "xpack.securitySolution.assetInventory.fleetIntegration.awsAccountType.awsOrganizationDescription": "通过为 Elastic 提供您的 AWS 组织的只读(配置)访问权限,将 Elastic 连接到您环境中的每个 AWS 帐户(当前和未来)。", "xpack.securitySolution.assetInventory.fleetIntegration.awsAccountType.awsOrganizationLabel": "AWS 组织", "xpack.securitySolution.assetInventory.fleetIntegration.awsAccountType.singleAccountLabel": "单个帐户", "xpack.securitySolution.assetInventory.fleetIntegration.awsAccountTypeDescriptionLabel": "选择单个帐户或组织,然后填写名称和描述以帮助标识此集成。", - "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.azureOrganizationDescription": "通过为 Elastic 提供您的 Azure 组织(租户根组)的只读(配置)访问权限,将 Elastic 连接到您环境中的每个 Azure 订阅(当前和未来)。", - "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.azureOrganizationLabel": "AWS 组织", - "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.singleAccountDescription": "部署到单个订阅适用于初始 POC。为确保全面覆盖,强烈建议在组织(租户根组)级别部署 CSPM,这会自动连接所有订阅(当前和未来)。", - "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.singleAccountLabel": "单个订阅", - "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountTypeDescriptionLabel": "选择载入 Azure 组织(租户根组)或单个 Azure 订阅,然后填写名称和描述以帮助标识此集成。", - "xpack.securitySolution.assetInventory.fleetIntegration.configureAssetIntegrationDescription": "选择您要监测的云服务提供商 (CSP),然后填写名称和描述以帮助标识此集成", - "xpack.securitySolution.assetInventory.fleetIntegration.editWarning.calloutTitle": "正在修改集成详情", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpOrganizationDescription": "通过为 Elastic 提供您的 GCP 组织的只读(配置)访问权限,将 Elastic 连接到您环境中的每个 GCP 项目(当前和未来)", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpOrganizationLabel": "GCP 组织", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpSingleAccountLabel": "单个项目", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountTypeDescriptionLabel": "选择单个项目或组织,然后填写名称和描述以帮助标识此集成。", - "xpack.securitySolution.assetInventory.fleetIntegration.integrationDescriptionLabel": "描述", - "xpack.securitySolution.assetInventory.fleetIntegration.integrationNameLabel": "名称", - "xpack.securitySolution.assetInventory.fleetIntegration.integrationSettingsTitle": "集成设置", - "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentBasedRadioDescription": "将 Elastic 代理部署到您的云环境", - "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentBasedRadioLabel": "基于代理", - "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentlessRadioLabel": "无代理", - "xpack.securitySolution.assetInventory.fleetIntegration.accessKeyIdLabel": "访问密钥 ID", - "xpack.securitySolution.assetInventory.fleetIntegration.assumeRoleDescription": "IAM 角色 Amazon 资源名称 (ARN) 是您可在 AWS 帐户中创建的 IAM 身份。创建 IAM 角色时,用户可以定义该角色的权限。角色没有标准的长期凭据,如密码或访问密钥。", - "xpack.securitySolution.assetInventory.fleetIntegration.assumeRoleLabel": "接管角色", "xpack.securitySolution.assetInventory.fleetIntegration.awsCredentialTypeSelectorLabel": "首选手动方法", "xpack.securitySolution.assetInventory.fleetIntegration.awsCredentialTypeSelectorLabelAgentless": "首选方法", - "xpack.securitySolution.assetInventory.fleetIntegration.credentialProfileNameLabel": "凭据配置文件名", - "xpack.securitySolution.assetInventory.fleetIntegration.directAccessKeyLabel": "直接访问密钥", - "xpack.securitySolution.assetInventory.fleetIntegration.directAccessKeysDescription": "访问密钥是 IAM 用户或 AWS 帐户根用户的长期凭据。", - "xpack.securitySolution.assetInventory.fleetIntegration.documentationLinkText": "文档", - "xpack.securitySolution.assetInventory.fleetIntegration.roleArnLabel": "角色 ARN", - "xpack.securitySolution.assetInventory.fleetIntegration.secretAccessKeyLabel": "机密访问密钥", - "xpack.securitySolution.assetInventory.fleetIntegration.sessionTokenLabel": "会话令牌", - "xpack.securitySolution.assetInventory.fleetIntegration.sharedCredentialFileLabel": "共享凭据文件", - "xpack.securitySolution.assetInventory.fleetIntegration.sharedCredentialLabel": "共享凭据", - "xpack.securitySolution.assetInventory.fleetIntegration.sharedCredentialsDescription": "如果对不同工具或应用程序使用不同的 AWS 凭据,可以使用配置文件在同一配置文件中定义多个访问密钥。", - "xpack.securitySolution.assetInventory.fleetIntegration.temporaryKeysDescription": "您可以在 AWS 中配置在指定持续时间内有效的临时安全凭据。它们包括访问密钥 ID、机密访问密钥和安全令牌(通常使用 GetSessionToken 查找)。", - "xpack.securitySolution.assetInventory.fleetIntegration.temporaryKeysLabel": "临时密钥", "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.cloudFormationSetupNote": "如需了解更多详细信息,请参阅{documentation}。", "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.cloudFormationSetupStep.hostRequirement": "确保在下面的“要将此集成添加到什么位置?”部分中选择了“新主机”", "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.cloudFormationSetupStep.launch": "在随后的弹出式模式窗口中,单击“启动 CloudFormation”按钮。", @@ -35304,6 +35285,11 @@ "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.cloudFormationSetupStep.save": "单击此页面底部右侧的“保存并继续”按钮", "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.setupFormatOptions.manual": "手动", "xpack.securitySolution.assetInventory.fleetIntegration.awsIntegration.setupInfoContentTitle": "设置访问权限", + "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.azureOrganizationDescription": "通过为 Elastic 提供您的 Azure 组织(租户根组)的只读(配置)访问权限,将 Elastic 连接到您环境中的每个 Azure 订阅(当前和未来)。", + "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.azureOrganizationLabel": "AWS 组织", + "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.singleAccountDescription": "部署到单个订阅适用于初始 POC。为确保全面覆盖,强烈建议在组织(租户根组)级别部署 CSPM,这会自动连接所有订阅(当前和未来)。", + "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountType.singleAccountLabel": "单个订阅", + "xpack.securitySolution.assetInventory.fleetIntegration.azureAccountTypeDescriptionLabel": "选择载入 Azure 组织(租户根组)或单个 Azure 订阅,然后填写名称和描述以帮助标识此集成。", "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.armTemplateSetupNote": "如需了解更多详细信息,请参阅{documentation}。", "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.armTemplateSetupStep.hostRequirement": "确保在下面的“要将此集成添加到什么位置?”部分中选择了“新主机”", "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.armTemplateSetupStep.launch": "在随后的弹出式模式窗口中,复制相关 Bash 命令,然后单击“启动 ARM 模板”按钮。", @@ -35328,36 +35314,45 @@ "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.setupFormatOptions.manual": "手动", "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.setupInfoContentTitle": "设置访问权限", "xpack.securitySolution.assetInventory.fleetIntegration.azureIntegration.tenantIdLabel": "租户 ID", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.agentlessAWSCredentialsForm.cloudFormation.launchButton": "启动 CloudFormation", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.description": "访问密钥是 IAM 用户或 AWS 帐户根用户的长期凭据。利用 AWS CloudFormation(内置 AWS 工具)或一系列手动步骤设置访问权限。{learnMore}。", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.learnMoreLinkText": "详细了解 CloudFormation", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.steps.launch": "单击下面的 {launchCloudFormation} 按钮。", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.steps.organizationLogin": "作为 {admin} 登录到您要载入的 AWS 组织管理帐户", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.guide.steps.singleLogin": "作为 {admin} 登录到您要载入的 AWS 帐户", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.accept": "在打开的 CloudFormation 堆栈复查表单中,勾选 {capabilities} 下方的复选框:{acknowledge}", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.accept.acknowledge": "我确认,AWS CloudFormation 可以创建 IAM 资源。", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.accept.capabilties": "功能", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.create": "单击 {createStack}。", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.credentials": "复制 {accessKeyId} 和 {secretAccessKey},然后粘贴以下凭据", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.region": "(可选)将右上角的 {amazonRegion} 更改为要将您的堆栈部署到的区域", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.cloudFormation.steps.stackStatus": "一旦堆栈状态为 {createComplete},则单击“输出”选项卡", - "xpack.securitySolution.assetInventory.fleetIntegration.agentlessForm.googleCloudShell.cloudCredentials.button": "启动 Google Cloud Shell", - "xpack.securitySolution.assetInventory.fleetIntegration.assetIntegration.integration.fieldRequired": "{field} 必填", + "xpack.securitySolution.assetInventory.fleetIntegration.configureAssetIntegrationDescription": "选择您要监测的云服务提供商 (CSP),然后填写名称和描述以帮助标识此集成", + "xpack.securitySolution.assetInventory.fleetIntegration.credentialProfileNameLabel": "凭据配置文件名", + "xpack.securitySolution.assetInventory.fleetIntegration.directAccessKeyLabel": "直接访问密钥", + "xpack.securitySolution.assetInventory.fleetIntegration.directAccessKeysDescription": "访问密钥是 IAM 用户或 AWS 帐户根用户的长期凭据。", + "xpack.securitySolution.assetInventory.fleetIntegration.documentationLinkText": "文档", + "xpack.securitySolution.assetInventory.fleetIntegration.editWarning.calloutTitle": "正在修改集成详情", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpOrganizationDescription": "通过为 Elastic 提供您的 GCP 组织的只读(配置)访问权限,将 Elastic 连接到您环境中的每个 GCP 项目(当前和未来)", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpOrganizationLabel": "GCP 组织", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountType.gcpSingleAccountLabel": "单个项目", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpAccountTypeDescriptionLabel": "选择单个项目或组织,然后填写名称和描述以帮助标识此集成。", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.cloudShellSetupStep.hostRequirement": "确保在下面的“要将此集成添加到什么位置?”部分中选择了“新主机”", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.cloudShellSetupStep.launch": "单击页面底部右侧的“保存并继续”。然后,在弹出式模式窗口中单击“启动 Google Cloud Shell”", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.cloudShellSetupStep.login": "登录到 Google Cloud 控制台", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.cloudShellSetupStep.save": "记下您要监测的项目的 GCP 项目 ID", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.credentialsFileOption": "凭据文件", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.credentialsJsonOption": "凭据 JSON", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialFileText": "包含用于订阅的凭据和密钥的 JSON 文件的路径", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialJSONText": "包含用于订阅的凭据和密钥的 JSON Blob", + "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialSelectBoxTitle": "凭据", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.organizationCloudShellSetupStep.save": "记下您要监测的组织的 GCP 组织 ID 和要在其中预配资源以用于监测目的的项目 ID,然后在下面的输入框中提供这些 ID", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.organizationIdFieldLabel": "组织 ID", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.projectidFieldLabel": "项目 ID", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.setupFormatOptions.googleCloudShell": "Google Cloud Shell", "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.setupFormatOptions.manual": "手动", + "xpack.securitySolution.assetInventory.fleetIntegration.integrationDescriptionLabel": "描述", + "xpack.securitySolution.assetInventory.fleetIntegration.integrationNameLabel": "名称", + "xpack.securitySolution.assetInventory.fleetIntegration.integrationSettingsTitle": "集成设置", + "xpack.securitySolution.assetInventory.fleetIntegration.roleArnLabel": "角色 ARN", + "xpack.securitySolution.assetInventory.fleetIntegration.secretAccessKeyLabel": "机密访问密钥", + "xpack.securitySolution.assetInventory.fleetIntegration.sessionTokenLabel": "会话令牌", + "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentBasedRadioDescription": "将 Elastic 代理部署到您的云环境", + "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentBasedRadioLabel": "基于代理", + "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnology.agentlessRadioLabel": "无代理", "xpack.securitySolution.assetInventory.fleetIntegration.setupTechnologySelector.deploymentOptionsTitle": "部署选项", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialFileText": "包含用于订阅的凭据和密钥的 JSON 文件的路径", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialJSONText": "包含用于订阅的凭据和密钥的 JSON Blob", - "xpack.securitySolution.assetInventory.fleetIntegration.gcpIntegration.gcpInputText.credentialSelectBoxTitle": "凭据", + "xpack.securitySolution.assetInventory.fleetIntegration.sharedCredentialFileLabel": "共享凭据文件", + "xpack.securitySolution.assetInventory.fleetIntegration.sharedCredentialLabel": "共享凭据", + "xpack.securitySolution.assetInventory.fleetIntegration.sharedCredentialsDescription": "如果对不同工具或应用程序使用不同的 AWS 凭据,可以使用配置文件在同一配置文件中定义多个访问密钥。", + "xpack.securitySolution.assetInventory.fleetIntegration.temporaryKeysDescription": "您可以在 AWS 中配置在指定持续时间内有效的临时安全凭据。它们包括访问密钥 ID、机密访问密钥和安全令牌(通常使用 GetSessionToken 查找)。", + "xpack.securitySolution.assetInventory.fleetIntegration.temporaryKeysLabel": "临时密钥", "xpack.securitySolution.assetInventory.searchBar.searchPlaceholder": "使用 KQL 语法筛选数据", "xpack.securitySolution.assetInventory.technicalPreviewLabel": "技术预览", "xpack.securitySolution.assetInventory.technicalPreviewTooltip": "此功能为实验性功能,尚不受支持。它可能会随时更改或被移除。", diff --git a/yarn.lock b/yarn.lock index ea2c8919a56e6..970cb32a98aff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5648,6 +5648,10 @@ version "0.0.0" uid "" +"@kbn/file-upload@link:x-pack/platform/packages/shared/file-upload": + version "0.0.0" + uid "" + "@kbn/files-example-plugin@link:examples/files_example": version "0.0.0" uid "" From 7bf51bfc73545d9224c06e076f50468be6ceb0cc Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 21 May 2025 15:38:56 +0000 Subject: [PATCH 06/16] [CI] Auto-commit changed files from 'node scripts/generate codeowners' --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 13859767f89fb..0b01118c4c209 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -819,6 +819,7 @@ x-pack/platform/packages/shared/ai-infra/inference-common @elastic/appex-ai-infr x-pack/platform/packages/shared/ai-infra/inference-langchain @elastic/appex-ai-infra x-pack/platform/packages/shared/ai-infra/product-doc-common @elastic/appex-ai-infra x-pack/platform/packages/shared/alerting-rule-utils @elastic/obs-ux-management-team +x-pack/platform/packages/shared/file-upload @elastic/ml-ui x-pack/platform/packages/shared/file-upload-common @elastic/ml-ui x-pack/platform/packages/shared/index-lifecycle-management/index_lifecycle_management_common_shared @elastic/kibana-management x-pack/platform/packages/shared/index-management/index_management_shared_types @elastic/kibana-management From 5b233c27fedac95c33fa80954b9ce88065d4cec1 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 22 May 2025 08:21:45 +0000 Subject: [PATCH 07/16] [CI] Auto-commit changed files from 'node scripts/yarn_deduplicate' --- .../packages/shared/file-upload-common/tsconfig.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/x-pack/platform/packages/shared/file-upload-common/tsconfig.json b/x-pack/platform/packages/shared/file-upload-common/tsconfig.json index 0d9466be36cca..37d1561f95b8d 100644 --- a/x-pack/platform/packages/shared/file-upload-common/tsconfig.json +++ b/x-pack/platform/packages/shared/file-upload-common/tsconfig.json @@ -14,10 +14,5 @@ "target/**/*" ], "kbn_references": [ - "@kbn/index-management-shared-types", - "@kbn/core", - "@kbn/data-views-plugin", - "@kbn/i18n", - "@kbn/data-plugin", ] } From 1dcc1d802be8687878a3f0e144cfb700981b6676 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 22 May 2025 10:08:19 +0000 Subject: [PATCH 08/16] [CI] Auto-commit changed files from 'node scripts/lint_packages --fix' --- docs/extend/plugin-list.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/extend/plugin-list.md b/docs/extend/plugin-list.md index 5649d96282826..ea704a8602af4 100644 --- a/docs/extend/plugin-list.md +++ b/docs/extend/plugin-list.md @@ -24,12 +24,12 @@ mapped_pages: | [contentManagement](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/content_management/README.md) | The content management plugin provides functionality to manage content in Kibana. | | [controls](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/controls/README.mdx) | The Controls plugin contains Embeddables which can be used to add user-friendly interactivity to apps. | | [customIntegrations](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/custom_integrations/README.md) | Register add-data cards | -| [dashboard](kibana-dashboard-plugin.md) | - Registers the dashboard application.
- Adds a dashboard embeddable that can be used in other applications. | +| [dashboard](kibana-dashboard-plugin.md) | - Registers the dashboard application. - Adds a dashboard embeddable that can be used in other applications. | | [data](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/data/README.mdx) | The data plugin provides common data access services, such as search and query, for solutions and application developers. | | [dataViewEditor](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/data_view_editor/README.md) | Create data views from within Kibana apps. | | [dataViewFieldEditor](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/data_view_field_editor/README.md) | The reusable field editor across Kibana! | | [dataViewManagement](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/data_view_management) | WARNING: Missing or empty README. | -| [dataViews](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/data_views/README.mdx) | The data views API provides a consistent method of structuring and formatting documents and field lists across the various Kibana apps. It's typically used in conjunction with `SearchSource` for composing queries. | +| [dataViews](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/data_views/README.mdx) | The data views API provides a consistent method of structuring and formatting documents and field lists across the various Kibana apps. It's typically used in conjunction with for composing queries. | | [devTools](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/dev_tools/README.md) | The ui/registry/dev_tools is removed in favor of the devTools plugin which exposes a register method in the setup contract. Registering app works mostly the same as registering apps in core.application.register. Routing will be handled by the id of the dev tool - your dev tool will be mounted when the URL matches /app/dev_tools#/. This API doesn't support angular, for registering angular dev tools, bootstrap a local module on mount into the given HTML element. | | [discover](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/discover/README.md) | Contains the Discover application and the saved search embeddable. | | [discoverShared](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/discover_shared/README.md) | A stateful layer to register shared features and provide an access point to discover without a direct dependency. | @@ -79,7 +79,7 @@ mapped_pages: | [telemetry](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/telemetry/README.md) | Telemetry allows Kibana features to have usage tracked in the wild. The general term "telemetry" refers to multiple things: | | [telemetryCollectionManager](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/telemetry_collection_manager/README.md) | Telemetry's collection manager to go through all the telemetry sources when fetching it before reporting. | | [telemetryManagementSection](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/telemetry_management_section/README.md) | This plugin adds the Advanced Settings section for the Usage and Security Data collection (aka Telemetry). | -| [uiActions](uiactions-plugin.md) | UI Actions plugins provides API to manage *triggers* and *actions*. *Trigger* is an abstract description of user's intent to perform an action (like user clicking on a value inside chart). It allows us to do runtime binding between code from different plugins. For, example one such trigger is when somebody applies filters on dashboard; another one is when somebody opens a Dashboard panel context menu. *Actions* are pieces of code that execute in response to a trigger. For example, to the dashboard filtering trigger multiple actions can be attached. Once a user filters on the dashboard all possible actions are displayed to the user in a popup menu and the user has to chose one.
In general this plugin provides:
- Creating custom functionality (actions).
- Creating custom user interaction events (triggers).
- Attaching and detaching actions to triggers.
- Emitting trigger events.
- Executing actions attached to a given trigger.
- Exposing a context menu for the user to choose the appropriate action when there are multiple actions attached to a single trigger. | +| [uiActions](uiactions-plugin.md) | UI Actions plugins provides API to manage *triggers* and *actions*. *Trigger* is an abstract description of user's intent to perform an action (like user clicking on a value inside chart). It allows us to do runtime binding between code from different plugins. For, example one such trigger is when somebody applies filters on dashboard; another one is when somebody opens a Dashboard panel context menu. *Actions* are pieces of code that execute in response to a trigger. For example, to the dashboard filtering trigger multiple actions can be attached. Once a user filters on the dashboard all possible actions are displayed to the user in a popup menu and the user has to chose one. In general this plugin provides: - Creating custom functionality (actions). - Creating custom user interaction events (triggers). - Attaching and detaching actions to triggers. - Emitting trigger events. - Executing actions attached to a given trigger. - Exposing a context menu for the user to choose the appropriate action when there are multiple actions attached to a single trigger. | | [uiActionsEnhanced](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/ui_actions_enhanced/README.md) | Registers commercially licensed generic actions like per panel time range and contains some code that supports drilldown work. | | [unifiedDocViewer](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/unified_doc_viewer/README.md) | This plugin contains services reliant on the plugin lifecycle for the unified doc viewer component (see @kbn/unified-doc-viewer). | | [unifiedSearch](https://github.com/elastic/kibana/blob/main/src/platform/plugins/shared/unified_search/README.md) | Contains all the components of Kibana's unified search experience. Specifically: | From f8313e667d2d39a56f8fdbfa6a81b22e39ec0224 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 22 May 2025 12:59:14 +0100 Subject: [PATCH 09/16] fixing js --- .../file_data_visualizer/components/edit_flyout/overrides.js | 2 +- .../components/edit_flyout/overrides.test.js | 2 +- .../file_data_visualizer/components/import_view/import_view.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/edit_flyout/overrides.js b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/edit_flyout/overrides.js index 95f3357073d73..d95a9650e69ff 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/edit_flyout/overrides.js +++ b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/edit_flyout/overrides.js @@ -23,7 +23,7 @@ import { EuiTextArea, } from '@elastic/eui'; -import { FILE_FORMATS, NO_TIME_FORMAT } from '../../../../../common/constants'; +import { FILE_FORMATS, NO_TIME_FORMAT } from '@kbn/file-upload-common'; import { getFormatOptions, diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/edit_flyout/overrides.test.js b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/edit_flyout/overrides.test.js index a0f5faaf46668..1733fdf841070 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/edit_flyout/overrides.test.js +++ b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/edit_flyout/overrides.test.js @@ -7,7 +7,7 @@ import { mountWithIntl } from '@kbn/test-jest-helpers'; import React from 'react'; -import { FILE_FORMATS } from '../../../../../common/constants'; +import { FILE_FORMATS } from '@kbn/file-upload-common'; import { Overrides } from './overrides'; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/import_view/import_view.js b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/import_view/import_view.js index 280e85e58dc14..6aed380f96d04 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/import_view/import_view.js +++ b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/import_view/import_view.js @@ -22,6 +22,7 @@ import { import { debounce } from 'lodash'; import { context } from '@kbn/kibana-react-plugin/public'; +import { FILE_FORMATS } from '@kbn/file-upload-common'; import { ResultsLinks } from '../../../common/components/results_links'; import { FilebeatConfigFlyout } from '../../../common/components/filebeat_config_flyout'; import { ImportProgress, IMPORT_STATUS } from '../import_progress'; @@ -36,7 +37,6 @@ import { } from '../../../common/components/combined_fields'; import { MODE as DATAVISUALIZER_MODE } from '../file_data_visualizer_view/constants'; import { importData } from './import'; -import { FILE_FORMATS } from '../../../../../common/constants'; const DEFAULT_INDEX_SETTINGS = {}; const CONFIG_MODE = { SIMPLE: 0, ADVANCED: 1 }; From 7bdf8e72d207cc1dc9a5d46b9b65891b500e40cc Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 27 May 2025 16:29:41 +0100 Subject: [PATCH 10/16] removing commented code --- .../shared/file-upload/file_upload_manager/merge_tools.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/x-pack/platform/packages/shared/file-upload/file_upload_manager/merge_tools.ts b/x-pack/platform/packages/shared/file-upload/file_upload_manager/merge_tools.ts index 7412978f26f28..6211016d3beae 100644 --- a/x-pack/platform/packages/shared/file-upload/file_upload_manager/merge_tools.ts +++ b/x-pack/platform/packages/shared/file-upload/file_upload_manager/merge_tools.ts @@ -24,9 +24,6 @@ export enum CLASH_ERROR_TYPE { WARNING, } -// export const NEW_FIELD_THRESHOLD = 10; -// export const UNUSED_FIELD_THRESHOLD = 10; - export interface MappingClash { fieldName: string; existingType: string; From 16d6633b3dbaf175f26cce45d58ace28457a868d Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 3 Jun 2025 11:36:57 +0000 Subject: [PATCH 11/16] [CI] Auto-commit changed files from 'node scripts/capture_oas_snapshot --include-path /api/status --include-path /api/alerting/rule/ --include-path /api/alerting/rules --include-path /api/actions --include-path /api/security/role --include-path /api/spaces --include-path /api/streams --include-path /api/fleet --include-path /api/dashboards --include-path /api/saved_objects/_import --include-path /api/saved_objects/_export --include-path /api/maintenance_window --update' --- oas_docs/bundle.serverless.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/oas_docs/bundle.serverless.json b/oas_docs/bundle.serverless.json index 9b69de6beba3d..4e2afd1b801b5 100644 --- a/oas_docs/bundle.serverless.json +++ b/oas_docs/bundle.serverless.json @@ -46211,7 +46211,7 @@ "tags": [ "maintenance-window" ], - "x-state": "Generally available; added in 9.1.0" + "x-state": "Generally available" } }, "/api/maintenance_window/{id}": { @@ -46257,7 +46257,7 @@ "tags": [ "maintenance-window" ], - "x-state": "Generally available; added in 9.1.0" + "x-state": "Generally available" }, "get": { "description": "[Required authorization] Route required privileges: read-maintenance-window.", @@ -46453,7 +46453,7 @@ "tags": [ "maintenance-window" ], - "x-state": "Generally available; added in 9.1.0" + "x-state": "Generally available" }, "patch": { "description": "[Required authorization] Route required privileges: write-maintenance-window.", @@ -46790,7 +46790,7 @@ "tags": [ "maintenance-window" ], - "x-state": "Generally available; added in 9.1.0" + "x-state": "Generally available" } }, "/api/maintenance_window/{id}/_archive": { @@ -46998,7 +46998,7 @@ "tags": [ "maintenance-window" ], - "x-state": "Generally available; added in 9.1.0" + "x-state": "Generally available" } }, "/api/maintenance_window/{id}/_unarchive": { @@ -47206,7 +47206,7 @@ "tags": [ "maintenance-window" ], - "x-state": "Generally available; added in 9.1.0" + "x-state": "Generally available" } }, "/api/saved_objects/_export": { From a009a48d0b26a79cf2b23be41b0f0884db1eef4a Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 3 Jun 2025 11:54:27 +0000 Subject: [PATCH 12/16] [CI] Auto-commit changed files from 'make api-docs' --- oas_docs/output/kibana.serverless.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index 3e1bce987ced3..eaf137f428689 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -42557,7 +42557,7 @@ paths: summary: Create a maintenance window. tags: - maintenance-window - x-state: Generally available; added in 9.1.0 + x-state: Generally available /api/maintenance_window/{id}: delete: description: '[Required authorization] Route required privileges: write-maintenance-window.' @@ -42588,7 +42588,7 @@ paths: summary: Delete a maintenance window. tags: - maintenance-window - x-state: Generally available; added in 9.1.0 + x-state: Generally available get: description: '[Required authorization] Route required privileges: read-maintenance-window.' operationId: get-maintenance-window-id @@ -42729,7 +42729,7 @@ paths: summary: Get maintenance window details. tags: - maintenance-window - x-state: Generally available; added in 9.1.0 + x-state: Generally available patch: description: '[Required authorization] Route required privileges: write-maintenance-window.' operationId: patch-maintenance-window-id @@ -42970,7 +42970,7 @@ paths: summary: Update a maintenance window. tags: - maintenance-window - x-state: Generally available; added in 9.1.0 + x-state: Generally available /api/maintenance_window/{id}/_archive: post: description: '[Required authorization] Route required privileges: write-maintenance-window.' @@ -43119,7 +43119,7 @@ paths: summary: Archive a maintenance window. tags: - maintenance-window - x-state: Generally available; added in 9.1.0 + x-state: Generally available /api/maintenance_window/{id}/_unarchive: post: description: '[Required authorization] Route required privileges: write-maintenance-window.' @@ -43268,7 +43268,7 @@ paths: summary: Unarchive a maintenance window. tags: - maintenance-window - x-state: Generally available; added in 9.1.0 + x-state: Generally available /api/ml/saved_objects/sync: get: description: | From bbabf59509234706f617c8f4979ca2b7b191bd8b Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 4 Jun 2025 09:35:06 +0100 Subject: [PATCH 13/16] changes based on review --- .../file_upload_manager/file_manager.ts | 59 ++++++------ .../file_upload_manager/file_size_check.ts | 2 +- .../file_upload_manager/file_wrapper.ts | 2 +- .../file-upload/file_upload_manager/index.ts | 2 + .../file_upload_manager/merge_tools.ts | 14 ++- .../shared/file-upload/src/use_file_upload.ts | 48 +++++++--- .../common/utils/tika_utils.ts | 90 ------------------- .../analysis_summary/analysis_summary.tsx | 2 +- .../file_data_visualizer_view.js | 5 +- .../file_error_callouts.tsx | 2 +- .../file_size_check.ts | 40 --------- .../public/lite/file_upload_lite.tsx | 9 +- .../public/lite/file_upload_lite_view.tsx | 4 +- .../private/file_upload/server/import_data.ts | 3 +- 14 files changed, 88 insertions(+), 194 deletions(-) delete mode 100644 x-pack/platform/plugins/private/data_visualizer/common/utils/tika_utils.ts delete mode 100644 x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_size_check.ts diff --git a/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_manager.ts b/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_manager.ts index 06ef25c63cb73..7edff9bd43b76 100644 --- a/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_manager.ts +++ b/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_manager.ts @@ -10,7 +10,7 @@ import type { FileUploadStartApi } from '@kbn/file-upload-plugin/public/api'; import type { Subscription } from 'rxjs'; import type { Observable } from 'rxjs'; import { switchMap, combineLatest, BehaviorSubject, of } from 'rxjs'; -import type { HttpSetup } from '@kbn/core/public'; +import type { HttpSetup, NotificationsStart } from '@kbn/core/public'; import type { IImporter } from '@kbn/file-upload-plugin/public/importer/types'; import type { DataViewsServicePublic } from '@kbn/data-views-plugin/public/types'; import type { @@ -26,6 +26,7 @@ import type { import { i18n } from '@kbn/i18n'; import { FileUploadResults } from '@kbn/file-upload-common'; +import { isEqual } from 'lodash'; import type { FileAnalysis } from './file_wrapper'; import { FileWrapper } from './file_wrapper'; @@ -50,17 +51,9 @@ export enum STATUS { export interface Config { json: T; - string: string; valid: boolean; - count: number; } -export const emptyConfig = { - json: {}, - string: '', - valid: false, -}; - export interface UploadStatus { analysisStatus: STATUS; overallImportStatus: STATUS; @@ -97,15 +90,11 @@ export class FileUploadManager { private mappingsCheckSubscription: Subscription; public readonly settings$ = new BehaviorSubject>({ json: {}, - string: '', valid: false, - count: 0, }); public readonly mappings$ = new BehaviorSubject>({ json: {}, - string: '', valid: false, - count: 0, }); public readonly existingIndexName$ = new BehaviorSubject(null); @@ -137,6 +126,7 @@ export class FileUploadManager { private fileUpload: FileUploadStartApi, private http: HttpSetup, private dataViewsContract: DataViewsServicePublic, + private notifications: NotificationsStart, private autoAddInferenceEndpointName: string | null = null, private autoCreateDataView: boolean = true, private removePipelinesAfterImport: boolean = true, @@ -166,7 +156,9 @@ export class FileUploadManager { if (uploadStatus.overallImportStatus === STATUS.STARTED) { return; } - if (this.getFiles().length === 0) { + const files = this.getFiles(); + + if (files.length === 0) { this.setStatus({ fileClashes: [], analysisStatus: STATUS.NOT_STARTED, @@ -175,7 +167,11 @@ export class FileUploadManager { } const { formatsOk, fileClashes } = this.getFormatClashes(); - const { mappingClashes, mergedMappings, existingIndexChecks } = this.createMergedMappings(); + + const { mappingClashes, mergedMappings, existingIndexChecks } = createMergedMappings( + files, + this.existingIndexMappings$.getValue() as FindFileStructureResponse['mappings'] + ); let mappingsOk = mappingClashes.length === 0; if (existingIndexChecks !== undefined) { @@ -319,14 +315,6 @@ export class FileUploadManager { }; } - private createMergedMappings() { - const files = this.getFiles(); - return createMergedMappings( - files, - this.existingIndexMappings$.getValue() as FindFileStructureResponse['mappings'] - ); - } - private getPipelines(): Array { const files = this.getFiles(); return files.map((file) => file.getPipeline()); @@ -344,7 +332,7 @@ export class FileUploadManager { }; } - public updatePipelines(pipelines: IngestPipeline[]) { + public updatePipelines(pipelines: Array) { const files = this.getFiles(); files.forEach((file, i) => { file.setPipeline(pipelines[i]); @@ -377,22 +365,18 @@ export class FileUploadManager { if (typeof config === 'string') { try { const json = JSON.parse(config); - const currentConfigString = JSON.stringify(currentConfig.json); - const incomingConfigString = JSON.stringify(json); this.setStatus({ [jsonValidKey]: true, }); - if (currentConfigString === incomingConfigString) { + if (isEqual(currentConfig.json, json)) { return; } config$.next({ json, - string: incomingConfigString, valid: true, - count: currentConfig.count + 1, }); } catch (e) { this.setStatus({ @@ -403,9 +387,7 @@ export class FileUploadManager { } else { config$.next({ json: config, - string: '', valid: true, - count: currentConfig.count + 1, }); } } @@ -669,6 +651,8 @@ export class FileUploadManager { }, }); }); + this.updateMappings(mappings); + this.updatePipelines(pipelines); } } @@ -689,6 +673,15 @@ export class FileUploadManager { this.existingIndexMappings$.next(mappings); } catch (e) { this.existingIndexMappings$.next(null); + this.notifications.toasts.addError(e, { + title: i18n.translate( + 'xpack.dataVisualizer.file.fileManager.errorLoadingExistingMappings', + { + defaultMessage: 'Error loading existing index mappings for {indexName}', + values: { indexName: existingIndexName }, + } + ), + }); } } } @@ -726,3 +719,7 @@ export function getInferenceId(mappings: MappingTypeMapping) { } return null; } + +export function semanticTextFieldExists(mappings: MappingTypeMapping) { + return Object.values(mappings.properties ?? {}).some((value) => value.type === 'semantic_text'); +} diff --git a/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_size_check.ts b/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_size_check.ts index 6c8b366d3d31a..fcca7319d404b 100644 --- a/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_size_check.ts +++ b/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_size_check.ts @@ -20,7 +20,7 @@ export class FileSizeChecker { : fileUpload.getMaxBytes(); } - public check(): boolean { + public isValid(): boolean { return this._fileSize <= this._maxBytes; } diff --git a/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_wrapper.ts b/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_wrapper.ts index c75fd18c2fefc..d8dcdc99133f3 100644 --- a/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_wrapper.ts +++ b/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_wrapper.ts @@ -99,7 +99,7 @@ export class FileWrapper { ...this.analyzedFile$.getValue(), fileName: this.file.name, loaded: false, - fileTooLarge: !this.fileSizeChecker.check(), + fileTooLarge: !this.fileSizeChecker.isValid(), fileSizeInfo: { fileSize: this.file.size, fileSizeFormatted: this.fileSizeChecker.fileSizeFormatted(), diff --git a/x-pack/platform/packages/shared/file-upload/file_upload_manager/index.ts b/x-pack/platform/packages/shared/file-upload/file_upload_manager/index.ts index f43165562fefa..7296c65e02334 100644 --- a/x-pack/platform/packages/shared/file-upload/file_upload_manager/index.ts +++ b/x-pack/platform/packages/shared/file-upload/file_upload_manager/index.ts @@ -8,3 +8,5 @@ export { FileUploadManager, STATUS, type Config, type UploadStatus } from './file_manager'; export { FileWrapper, type FileAnalysis } from './file_wrapper'; export { CLASH_ERROR_TYPE, CLASH_TYPE, type FileClash, type MappingClash } from './merge_tools'; +export { FileSizeChecker } from './file_size_check'; +export { isTikaType } from './tika_utils'; diff --git a/x-pack/platform/packages/shared/file-upload/file_upload_manager/merge_tools.ts b/x-pack/platform/packages/shared/file-upload/file_upload_manager/merge_tools.ts index 6211016d3beae..3deab49574c8f 100644 --- a/x-pack/platform/packages/shared/file-upload/file_upload_manager/merge_tools.ts +++ b/x-pack/platform/packages/shared/file-upload/file_upload_manager/merge_tools.ts @@ -7,6 +7,7 @@ import type { FindFileStructureResponse } from '@kbn/file-upload-plugin/common/types'; import type { MappingPropertyBase, MappingTypeMapping } from '@elastic/elasticsearch/lib/api/types'; +import { isEqual } from 'lodash'; import type { FileAnalysis, FileWrapper } from './file_wrapper'; export enum CLASH_TYPE { @@ -67,14 +68,9 @@ export function createMergedMappings( const mappings = files.map((file) => file.getMappings() ?? { properties: {} }); - // stringify each mappings and see if they are the same, if so return the first one. - // otherwise drill down and extract each field with it's type. - const mappingsString = mappings.map((m) => JSON.stringify(m)); - const mappingComparator = checkExistingIndexMappings - ? JSON.stringify(existingIndexMappings) - : mappingsString[0]; - - if (mappingsString.every((m) => m === mappingComparator)) { + // compare the mappings of all files to see if they are all the same + // if they are, return early + if (mappings.every((m) => isEqual(m, mappings[0]))) { return { mergedMappings: mappings[0] as MappingTypeMapping, mappingClashes: [] }; } @@ -167,7 +163,7 @@ export function createMergedMappings( const existingType = existingField.type; if (existingType !== field.value.type) { if (existingType === 'text' && field.value.type === 'keyword') { - // do nothing + // the existing field is text and the new field is keyword, we can keep the existing field type } else { existingIndexChecks.mappingClashes.push({ fieldName: field.name, diff --git a/x-pack/platform/packages/shared/file-upload/src/use_file_upload.ts b/x-pack/platform/packages/shared/file-upload/src/use_file_upload.ts index bb78d07d092b0..aef7e2f8779c1 100644 --- a/x-pack/platform/packages/shared/file-upload/src/use_file_upload.ts +++ b/x-pack/platform/packages/shared/file-upload/src/use_file_upload.ts @@ -9,9 +9,10 @@ import { createContext, useCallback, useContext, useEffect, useMemo, useState } import useObservable from 'react-use/lib/useObservable'; import { i18n } from '@kbn/i18n'; import type { Index } from '@kbn/index-management-shared-types/src/types'; -import type { ApplicationStart, HttpSetup } from '@kbn/core/public'; +import type { ApplicationStart, HttpSetup, NotificationsStart } from '@kbn/core/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { FileUploadResults } from '@kbn/file-upload-common'; +import useMountedState from 'react-use/lib/useMountedState'; import { CLASH_ERROR_TYPE, FileUploadManager, STATUS } from '../file_upload_manager'; export enum UPLOAD_TYPE { @@ -23,9 +24,11 @@ export function useFileUpload( fileUploadManager: FileUploadManager, data: DataPublicPluginStart, application: ApplicationStart, - onUploadComplete?: (results: FileUploadResults | null) => void, - http?: HttpSetup + http: HttpSetup, + notifications: NotificationsStart, + onUploadComplete?: (results: FileUploadResults | null) => void ) { + const isMounted = useMountedState(); const { dataViews } = data; const { navigateToApp } = application; @@ -74,15 +77,21 @@ export function useFileUpload( } http.get('/api/index_management/indices').then((indx) => { + if (!isMounted()) { + return; + } setIndices(indx.filter((i) => i.hidden === false && i.isFrozen === false)); }); - }, [http, fileUploadManager]); + }, [http, fileUploadManager, isMounted]); useEffect(() => { dataViews.getTitles().then((titles) => { + if (!isMounted()) { + return; + } setExistingDataViewNames(titles); }); - }, [dataViews]); + }, [dataViews, isMounted]); useEffect(() => { return () => { @@ -99,22 +108,39 @@ export function useFileUpload( setDataViewNameError(isDataViewNameValid(dataViewName, existingDataViewNames, indexName)); }, [dataViewName, existingDataViewNames, indexName]); - const uploadInProgress = + const uploadStarted = uploadStatus.overallImportStatus === STATUS.STARTED || uploadStatus.overallImportStatus === STATUS.COMPLETED || uploadStatus.overallImportStatus === STATUS.FAILED; - const onImportClick = useCallback(() => { + const onImportClick = useCallback(async () => { const existingIndex = fileUploadManager.getExistingIndexName(); const index = existingIndex !== null ? existingIndex : indexName; const dv = dataViewName === '' ? undefined : dataViewName; - fileUploadManager.import(index, dv).then((res) => { + try { + const res = await fileUploadManager.import(index, dv); + if (!isMounted()) { + return; + } if (onUploadComplete && res) { onUploadComplete(res); } setImportResults(res); - }); - }, [dataViewName, fileUploadManager, indexName, onUploadComplete]); + } catch (e) { + notifications.toasts.addError(e, { + title: i18n.translate('xpack.dataVisualizer.file.importView.importErrorNotificationTitle', { + defaultMessage: 'Error performing import', + }), + }); + } + }, [ + dataViewName, + fileUploadManager, + indexName, + isMounted, + notifications.toasts, + onUploadComplete, + ]); const existingIndexName = useObservable( fileUploadManager.existingIndexName$, @@ -176,7 +202,7 @@ export function useFileUpload( uploadStatus, fileClashes, fullFileUpload, - uploadInProgress, + uploadStarted, onImportClick, canImport, mappings, diff --git a/x-pack/platform/plugins/private/data_visualizer/common/utils/tika_utils.ts b/x-pack/platform/plugins/private/data_visualizer/common/utils/tika_utils.ts deleted file mode 100644 index 934378464d70a..0000000000000 --- a/x-pack/platform/plugins/private/data_visualizer/common/utils/tika_utils.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; - -export function isTikaType(type: string) { - return getTikaDisplayType(type).isTikaType; -} - -export const getTikaDisplayType = (type: string): { isTikaType: boolean; label: string } => { - switch (type) { - case 'application/doc': - case 'application/ms-doc': - case 'application/msword': - case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': - return { - isTikaType: true, - label: i18n.translate('xpack.dataVisualizer.file.tikaTypes.word', { - defaultMessage: 'Microsoft Office Word document', - }), - }; - - case 'application/excel': - case 'application/vnd.ms-excel': - case 'application/x-excel': - case 'application/x-msexcel': - case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': - return { - isTikaType: true, - label: i18n.translate('xpack.dataVisualizer.file.tikaTypes.excel', { - defaultMessage: 'Microsoft Office Excel document', - }), - }; - - case 'application/mspowerpoint': - case 'application/powerpoint': - case 'application/vnd.ms-powerpoint': - case 'application/x-mspowerpoint': - case 'application/vnd.openxmlformats-officedocument.presentationml.presentation': - return { - isTikaType: true, - label: i18n.translate('xpack.dataVisualizer.file.tikaTypes.powerPoint', { - defaultMessage: 'Microsoft Office Power Point document', - }), - }; - - case 'application/vnd.oasis.opendocument.presentation': - case 'application/vnd.oasis.opendocument.spreadsheet': - case 'application/vnd.oasis.opendocument.text': - return { - isTikaType: true, - label: i18n.translate('xpack.dataVisualizer.file.tikaTypes.openDoc', { - defaultMessage: 'Open Document Format', - }), - }; - - case 'text/rtf': - case 'application/rtf': - return { - isTikaType: true, - label: i18n.translate('xpack.dataVisualizer.file.tikaTypes.richText', { - defaultMessage: 'Rich Text Format', - }), - }; - - case 'application/pdf': - return { - isTikaType: true, - label: i18n.translate('xpack.dataVisualizer.file.tikaTypes.pdf', { - defaultMessage: 'PDF', - }), - }; - - case 'text/plain': - case 'text/plain; charset=UTF-8': - return { - isTikaType: true, - label: i18n.translate('xpack.dataVisualizer.file.tikaTypes.plainText', { - defaultMessage: 'Plain text', - }), - }; - - default: - return { isTikaType: false, label: type }; - } -}; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/analysis_summary/analysis_summary.tsx b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/analysis_summary/analysis_summary.tsx index e107ac037b1c3..cdfe81aa7d00f 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/analysis_summary/analysis_summary.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/analysis_summary/analysis_summary.tsx @@ -12,7 +12,7 @@ import React from 'react'; import { EuiTitle, EuiSpacer, EuiDescriptionList } from '@elastic/eui'; import type { FindFileStructureResponse } from '@kbn/file-upload-plugin/common'; import { FILE_FORMATS } from '@kbn/file-upload-common'; -import { getTikaDisplayType } from '../../../../../common/utils/tika_utils'; +import { getTikaDisplayType } from '@kbn/file-upload/file_upload_manager/tika_utils'; interface Props { results: FindFileStructureResponse; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_data_visualizer_view.js b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_data_visualizer_view.js index fe3f4621c1e7f..c9b0c2ad57325 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_data_visualizer_view.js +++ b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_data_visualizer_view.js @@ -11,6 +11,7 @@ import React, { Component } from 'react'; import { EuiSpacer } from '@elastic/eui'; import { isEqual } from 'lodash'; +import { FileSizeChecker, isTikaType } from '@kbn/file-upload/file_upload_manager'; import { AboutPanel, LoadingPanel } from '../about_panel'; import { ResultsView } from '../results_view'; @@ -31,8 +32,6 @@ import { import { analyzeTikaFile } from './tika_analyzer'; import { MODE } from './constants'; -import { FileSizeChecker } from './file_size_check'; -import { isTikaType } from '../../../../../common/utils/tika_utils'; export class FileDataVisualizerView extends Component { constructor(props) { @@ -103,7 +102,7 @@ export class FileDataVisualizerView extends Component { async loadFile(file) { this.fileSizeChecker = new FileSizeChecker(this.props.fileUpload, file); - if (this.fileSizeChecker.check()) { + if (this.fileSizeChecker.isValid()) { try { const { data, fileContents } = await readFile(file); if (isTikaType(file.type)) { diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_error_callouts.tsx b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_error_callouts.tsx index 13c4ed5f7336f..75c207b226fe2 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_error_callouts.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_error_callouts.tsx @@ -12,7 +12,7 @@ import React from 'react'; import { EuiCallOut, EuiSpacer, EuiButtonEmpty, EuiHorizontalRule } from '@elastic/eui'; import type { FindFileStructureErrorResponse } from '@kbn/file-upload-plugin/common'; -import type { FileSizeChecker } from './file_size_check'; +import type { FileSizeChecker } from '@kbn/file-upload/file_upload_manager/file_size_check'; interface FileTooLargeProps { fileSizeChecker: FileSizeChecker; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_size_check.ts b/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_size_check.ts deleted file mode 100644 index f9fd7bd8fd0f1..0000000000000 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_size_check.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { FileUploadStartApi } from '@kbn/file-upload-plugin/public/api'; -import numeral from '@elastic/numeral'; -import { FILE_SIZE_DISPLAY_FORMAT } from '@kbn/file-upload-common'; -import { isTikaType } from '../../../../../common/utils/tika_utils'; - -export class FileSizeChecker { - private _maxBytes: number; - private _fileSize: number; - constructor(fileUpload: FileUploadStartApi, file: File) { - this._fileSize = file.size; - this._maxBytes = isTikaType(file.type) - ? fileUpload.getMaxTikaBytes() - : fileUpload.getMaxBytes(); - } - - public check(): boolean { - return this._fileSize <= this._maxBytes; - } - - public maxBytes(): number { - return this._maxBytes; - } - - public fileSizeFormatted(): string { - return numeral(this._fileSize).format(FILE_SIZE_DISPLAY_FORMAT); - } - public maxFileSizeFormatted(): string { - return numeral(this._maxBytes).format(FILE_SIZE_DISPLAY_FORMAT); - } - public fileSizeDiffFormatted(): string { - return numeral(this._fileSize - this._maxBytes).format(FILE_SIZE_DISPLAY_FORMAT); - } -} diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite.tsx index 983eaafddcf05..d8800dfc5de14 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite.tsx @@ -37,7 +37,7 @@ export const FileDataVisualizerLite: FC = ({ ...coreStart, ...plugins, }; - const { data, fileUpload, cloud } = services; + const { data, fileUpload, cloud, notifications } = services; const { existingIndex, autoAddInference, autoCreateDataView, indexSettings, onUploadComplete } = props; @@ -47,6 +47,7 @@ export const FileDataVisualizerLite: FC = ({ fileUpload, coreStart.http, data.dataViews, + notifications, autoAddInference ?? null, autoCreateDataView, true, @@ -61,6 +62,7 @@ export const FileDataVisualizerLite: FC = ({ existingIndex, fileUpload, indexSettings, + notifications, ] ); @@ -68,8 +70,9 @@ export const FileDataVisualizerLite: FC = ({ fileUploadManager, data, coreStart.application, - onUploadComplete, - coreStart.http + coreStart.http, + notifications, + onUploadComplete ); const EmptyContext: FC> = ({ children }) => <>{children}; diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite_view.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite_view.tsx index 74501ea02b456..1dca84e2a0b8c 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite_view.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_upload_lite_view.tsx @@ -50,7 +50,7 @@ export const FileUploadLiteView: FC = ({ props, onClose }) => { uploadStatus, fileClashes, fullFileUpload, - uploadInProgress, + uploadStarted, onImportClick, canImport, setIndexName, @@ -160,7 +160,7 @@ export const FileUploadLiteView: FC = ({ props, onClose }) => { ) : null} - {uploadInProgress ? ( + {uploadStarted ? ( <> diff --git a/x-pack/platform/plugins/private/file_upload/server/import_data.ts b/x-pack/platform/plugins/private/file_upload/server/import_data.ts index bb1c31f3c3642..5f02d7e6d4f7b 100644 --- a/x-pack/platform/plugins/private/file_upload/server/import_data.ts +++ b/x-pack/platform/plugins/private/file_upload/server/import_data.ts @@ -14,6 +14,7 @@ import type { } from '@elastic/elasticsearch/lib/api/types'; import { INDEX_META_DATA_CREATED_BY } from '@kbn/file-upload-common'; +import { isEqual } from 'lodash'; import type { ImportResponse, ImportFailure, @@ -141,7 +142,7 @@ export function importDataProvider({ asCurrentUser }: IScopedClusterClient) { async function updateMappings(index: string, mappings: MappingTypeMapping) { const resp = await asCurrentUser.indices.getMapping({ index }); const existingMappings = resp[index]?.mappings; - if (JSON.stringify(existingMappings.properties) !== JSON.stringify(mappings.properties)) { + if (!isEqual(existingMappings.properties, mappings.properties)) { await asCurrentUser.indices.putMapping({ index, ...mappings }); } } From 5c6e3cd7a02a73667da8393c9d6825925d24ba32 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 5 Jun 2025 11:24:43 +0100 Subject: [PATCH 14/16] translations --- .../translations/translations/fr-FR.json | 131 +++++++++--------- .../translations/translations/ja-JP.json | 131 +++++++++--------- .../translations/translations/zh-CN.json | 131 +++++++++--------- 3 files changed, 186 insertions(+), 207 deletions(-) diff --git a/x-pack/platform/plugins/private/translations/translations/fr-FR.json b/x-pack/platform/plugins/private/translations/translations/fr-FR.json index ec02549292c98..ece8b3d54e527 100644 --- a/x-pack/platform/plugins/private/translations/translations/fr-FR.json +++ b/x-pack/platform/plugins/private/translations/translations/fr-FR.json @@ -16011,13 +16011,6 @@ "xpack.dataVisualizer.file.simpleImportSettings.indexNameAriaLabel": "Nom d'index, champ requis", "xpack.dataVisualizer.file.simpleImportSettings.indexNameFormRowLabel": "Nom de l'index", "xpack.dataVisualizer.file.simpleImportSettings.indexNamePlaceholder": "nom de l'index", - "xpack.dataVisualizer.file.tikaTypes.excel": "Document Excel de Microsoft Office", - "xpack.dataVisualizer.file.tikaTypes.openDoc": "Format du document ouvert", - "xpack.dataVisualizer.file.tikaTypes.pdf": "PDF", - "xpack.dataVisualizer.file.tikaTypes.plainText": "Texte brut", - "xpack.dataVisualizer.file.tikaTypes.powerPoint": "Document PowerPoint de Microsoft Office", - "xpack.dataVisualizer.file.tikaTypes.richText": "Format RTF", - "xpack.dataVisualizer.file.tikaTypes.word": "Document Word de Microsoft Office", "xpack.dataVisualizer.file.uploadView.closeButton": "Fermer", "xpack.dataVisualizer.file.uploadView.importButton": "Importer", "xpack.dataVisualizer.file.uploadView.importingButton": "Importation en cours", @@ -41040,6 +41033,68 @@ "xpack.securitySolution.system.withResultDescription": "avec le résultat", "xpack.securitySolution.tables.rowItemHelper.moreDescription": "plus non affiché", "xpack.securitySolution.tables.rowItemHelper.overflowButtonDescription": "+ {count} de plus", + "xpack.securitySolution.threatIntelligence.addToBlockList": "Ajouter une entrée dans la liste noire", + "xpack.securitySolution.threatIntelligence.addToExistingCase": "Ajouter à un cas existant", + "xpack.securitySolution.threatIntelligence.addToNewCase": "Ajouter au nouveau cas", + "xpack.securitySolution.threatIntelligence.blocklist.flyoutTitle": "Ajouter une liste noire", + "xpack.securitySolution.threatIntelligence.cases.eventDescription": "ajouté un indicateur de compromis", + "xpack.securitySolution.threatIntelligence.cases.indicatorFeedName": "Nom du fil :", + "xpack.securitySolution.threatIntelligence.cases.indicatorName": "Nom de l'indicateur :", + "xpack.securitySolution.threatIntelligence.cases.indicatorType": "Type d'indicateur :", + "xpack.securitySolution.threatIntelligence.common.emptyPage.body1": "Elastic Threat Intelligence facilite l'analyse et l'investigation des menaces potentielles pour la sécurité en regroupant les données de plusieurs sources en un seul endroit.", + "xpack.securitySolution.threatIntelligence.common.emptyPage.body2": "Vous pourrez consulter les données de tous les flux Threat Intelligence activés et prendre des mesures à partir de cette page.", + "xpack.securitySolution.threatIntelligence.common.emptyPage.body3": "Pour vous lancer avec Elastic Threat Intelligence, activez une ou plusieurs intégrations Threat Intelligence depuis la page Intégrations ou bien ingérez des données avec Filebeat. Pour plus d'informations, consultez la ressource {docsLink}.", + "xpack.securitySolution.threatIntelligence.common.emptyPage.buttonText": "Ajouter des intégrations", + "xpack.securitySolution.threatIntelligence.common.emptyPage.docsLinkText": "Sécurité de la documentation", + "xpack.securitySolution.threatIntelligence.common.emptyPage.imgAlt": "Activer les intégrations Threat Intelligence", + "xpack.securitySolution.threatIntelligence.common.emptyPage.title": "Prise en main d’Elastic Threat Intelligence", + "xpack.securitySolution.threatIntelligence.empty.description": "Essayer de rechercher sur une période plus longue ou de modifier votre recherche", + "xpack.securitySolution.threatIntelligence.empty.title": "Aucun résultat ne correspond à vos critères de recherche.", + "xpack.securitySolution.threatIntelligence.field.@timestamp": "@timestamp", + "xpack.securitySolution.threatIntelligence.field.threat.feed.name": "Fil", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.confidence": "Confiance", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.first_seen": "Vu en premier", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.last_seen": "Vu en dernier", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.marking.tlp": "Marquage TLP", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.name": "Indicateur", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.type": "Type d’indicateur", + "xpack.securitySolution.threatIntelligence.indicator.barChart.popover": "Plus d'actions", + "xpack.securitySolution.threatIntelligence.indicator.barchartSection.title": "Tendance", + "xpack.securitySolution.threatIntelligence.indicator.fieldSelector.label": "Empiler par", + "xpack.securitySolution.threatIntelligence.indicator.fieldsTable.fieldColumnLabel": "Champ", + "xpack.securitySolution.threatIntelligence.indicator.fieldsTable.valueColumnLabel": "Valeur", + "xpack.securitySolution.threatIntelligence.indicator.flyout.jsonTabLabel": "JSON", + "xpack.securitySolution.threatIntelligence.indicator.flyout.overviewTabLabel": "Aperçu", + "xpack.securitySolution.threatIntelligence.indicator.flyout.panelSubTitle": "Vu en premier :", + "xpack.securitySolution.threatIntelligence.indicator.flyout.panelTitleWithOverviewTab": "Détails de l'indicateur", + "xpack.securitySolution.threatIntelligence.indicator.flyout.tableTabLabel": "Tableau", + "xpack.securitySolution.threatIntelligence.indicator.flyoutOverviewTable.highlightedFields": "Champs en surbrillance", + "xpack.securitySolution.threatIntelligence.indicator.flyoutOverviewTable.viewAllFieldsInTable": "Afficher tous les champs dans le tableau", + "xpack.securitySolution.threatIntelligence.indicator.flyoutTable.errorMessageBody": "Une erreur s'est produite lors de l'affichage des champs et des valeurs des indicateurs.", + "xpack.securitySolution.threatIntelligence.indicator.flyoutTable.errorMessageTitle": "Impossible d'afficher les informations des indicateurs", + "xpack.securitySolution.threatIntelligence.indicator.table.actionColumnLabel": "Actions", + "xpack.securitySolution.threatIntelligence.indicator.table.moreActions": "Plus d'actions", + "xpack.securitySolution.threatIntelligence.indicator.table.viewDetailsButton": "Afficher les détails", + "xpack.securitySolution.threatIntelligence.indicatorNameFieldDescription": "Nom d'affichage de l'indicateur généré pendant le temps d'exécution", + "xpack.securitySolution.threatIntelligence.indicators.flyout.take-action.button": "Entreprendre une action", + "xpack.securitySolution.threatIntelligence.indicators.table.copyToClipboardLabel": "Copier dans le presse-papiers", + "xpack.securitySolution.threatIntelligence.inspectorFlyoutTitle": "Requêtes de recherche des indicateurs", + "xpack.securitySolution.threatIntelligence.inspectTitle": "Inspecter", + "xpack.securitySolution.threatIntelligence.investigateInTimelineButton": "Investiguer dans la chronologie", + "xpack.securitySolution.threatIntelligence.more-actions.popover": "Plus d'actions", + "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemDescription": "Elastic Threat Intelligence vous aide à voir si vous êtes exposé ou avez été exposé à des menaces connues actuelles ou passées.", + "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemKeywords": "Indicateurs", + "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemLabel": "Indicateurs", + "xpack.securitySolution.threatIntelligence.navigation.intelligenceNavItemLabel": "Intelligence", + "xpack.securitySolution.threatIntelligence.paywall.body": "Démarrez un essai gratuit ou mettez à niveau votre licence vers Enterprise pour utiliser la Threat Intelligence.", + "xpack.securitySolution.threatIntelligence.paywall.title": "Toujours plus avec Security !", + "xpack.securitySolution.threatIntelligence.paywall.trial": "Démarrer un essai gratuit", + "xpack.securitySolution.threatIntelligence.paywall.upgrade": "Mettre à niveau", + "xpack.securitySolution.threatIntelligence.queryBar.filterOut": "Exclure", + "xpack.securitySolution.threatIntelligence.timeline.addToTimeline": "Ajouter à la chronologie", + "xpack.securitySolution.threatIntelligence.timeline.investigateInTimelineButtonIcon": "Investiguer dans la chronologie", + "xpack.securitySolution.threatIntelligence.updateStatus.updated": "Mis à jour", + "xpack.securitySolution.threatIntelligence.updateStatus.updating": "Mise à jour...", "xpack.securitySolution.threatMatch.andDescription": "AND", "xpack.securitySolution.threatMatch.fieldDescription": "Champ", "xpack.securitySolution.threatMatch.fieldPlaceholderDescription": "Rechercher", @@ -46103,68 +46158,6 @@ "xpack.synthetics.windowValueExpression.numberOfChecksPopoverTitleLabel": "Nombre de vérifications", "xpack.synthetics.windowValueExpression.numberOfLocPopoverTitleLabel": "Nombre d'emplacements", "xpack.synthetics.windowValueExpression.percentLabel": "{numberOfLocations} {numberOfLocations, plural, one {emplacement} other {emplacements}}", - "xpack.securitySolution.threatIntelligence.addToBlockList": "Ajouter une entrée dans la liste noire", - "xpack.securitySolution.threatIntelligence.addToExistingCase": "Ajouter à un cas existant", - "xpack.securitySolution.threatIntelligence.addToNewCase": "Ajouter au nouveau cas", - "xpack.securitySolution.threatIntelligence.blocklist.flyoutTitle": "Ajouter une liste noire", - "xpack.securitySolution.threatIntelligence.cases.eventDescription": "ajouté un indicateur de compromis", - "xpack.securitySolution.threatIntelligence.cases.indicatorFeedName": "Nom du fil :", - "xpack.securitySolution.threatIntelligence.cases.indicatorName": "Nom de l'indicateur :", - "xpack.securitySolution.threatIntelligence.cases.indicatorType": "Type d'indicateur :", - "xpack.securitySolution.threatIntelligence.common.emptyPage.body1": "Elastic Threat Intelligence facilite l'analyse et l'investigation des menaces potentielles pour la sécurité en regroupant les données de plusieurs sources en un seul endroit.", - "xpack.securitySolution.threatIntelligence.common.emptyPage.body2": "Vous pourrez consulter les données de tous les flux Threat Intelligence activés et prendre des mesures à partir de cette page.", - "xpack.securitySolution.threatIntelligence.common.emptyPage.body3": "Pour vous lancer avec Elastic Threat Intelligence, activez une ou plusieurs intégrations Threat Intelligence depuis la page Intégrations ou bien ingérez des données avec Filebeat. Pour plus d'informations, consultez la ressource {docsLink}.", - "xpack.securitySolution.threatIntelligence.common.emptyPage.buttonText": "Ajouter des intégrations", - "xpack.securitySolution.threatIntelligence.common.emptyPage.docsLinkText": "Sécurité de la documentation", - "xpack.securitySolution.threatIntelligence.common.emptyPage.imgAlt": "Activer les intégrations Threat Intelligence", - "xpack.securitySolution.threatIntelligence.common.emptyPage.title": "Prise en main d’Elastic Threat Intelligence", - "xpack.securitySolution.threatIntelligence.empty.description": "Essayer de rechercher sur une période plus longue ou de modifier votre recherche", - "xpack.securitySolution.threatIntelligence.empty.title": "Aucun résultat ne correspond à vos critères de recherche.", - "xpack.securitySolution.threatIntelligence.field.@timestamp": "@timestamp", - "xpack.securitySolution.threatIntelligence.field.threat.feed.name": "Fil", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.confidence": "Confiance", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.first_seen": "Vu en premier", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.last_seen": "Vu en dernier", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.marking.tlp": "Marquage TLP", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.name": "Indicateur", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.type": "Type d’indicateur", - "xpack.securitySolution.threatIntelligence.indicator.barChart.popover": "Plus d'actions", - "xpack.securitySolution.threatIntelligence.indicator.barchartSection.title": "Tendance", - "xpack.securitySolution.threatIntelligence.indicator.fieldSelector.label": "Empiler par", - "xpack.securitySolution.threatIntelligence.indicator.fieldsTable.fieldColumnLabel": "Champ", - "xpack.securitySolution.threatIntelligence.indicator.fieldsTable.valueColumnLabel": "Valeur", - "xpack.securitySolution.threatIntelligence.indicator.flyout.jsonTabLabel": "JSON", - "xpack.securitySolution.threatIntelligence.indicator.flyout.overviewTabLabel": "Aperçu", - "xpack.securitySolution.threatIntelligence.indicator.flyout.panelSubTitle": "Vu en premier :", - "xpack.securitySolution.threatIntelligence.indicator.flyout.panelTitleWithOverviewTab": "Détails de l'indicateur", - "xpack.securitySolution.threatIntelligence.indicator.flyout.tableTabLabel": "Tableau", - "xpack.securitySolution.threatIntelligence.indicator.flyoutOverviewTable.highlightedFields": "Champs en surbrillance", - "xpack.securitySolution.threatIntelligence.indicator.flyoutOverviewTable.viewAllFieldsInTable": "Afficher tous les champs dans le tableau", - "xpack.securitySolution.threatIntelligence.indicator.flyoutTable.errorMessageBody": "Une erreur s'est produite lors de l'affichage des champs et des valeurs des indicateurs.", - "xpack.securitySolution.threatIntelligence.indicator.flyoutTable.errorMessageTitle": "Impossible d'afficher les informations des indicateurs", - "xpack.securitySolution.threatIntelligence.indicator.table.actionColumnLabel": "Actions", - "xpack.securitySolution.threatIntelligence.indicator.table.moreActions": "Plus d'actions", - "xpack.securitySolution.threatIntelligence.indicator.table.viewDetailsButton": "Afficher les détails", - "xpack.securitySolution.threatIntelligence.indicatorNameFieldDescription": "Nom d'affichage de l'indicateur généré pendant le temps d'exécution", - "xpack.securitySolution.threatIntelligence.indicators.flyout.take-action.button": "Entreprendre une action", - "xpack.securitySolution.threatIntelligence.indicators.table.copyToClipboardLabel": "Copier dans le presse-papiers", - "xpack.securitySolution.threatIntelligence.inspectorFlyoutTitle": "Requêtes de recherche des indicateurs", - "xpack.securitySolution.threatIntelligence.inspectTitle": "Inspecter", - "xpack.securitySolution.threatIntelligence.investigateInTimelineButton": "Investiguer dans la chronologie", - "xpack.securitySolution.threatIntelligence.more-actions.popover": "Plus d'actions", - "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemDescription": "Elastic Threat Intelligence vous aide à voir si vous êtes exposé ou avez été exposé à des menaces connues actuelles ou passées.", - "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemKeywords": "Indicateurs", - "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemLabel": "Indicateurs", - "xpack.securitySolution.threatIntelligence.navigation.intelligenceNavItemLabel": "Intelligence", - "xpack.securitySolution.threatIntelligence.paywall.body": "Démarrez un essai gratuit ou mettez à niveau votre licence vers Enterprise pour utiliser la Threat Intelligence.", - "xpack.securitySolution.threatIntelligence.paywall.title": "Toujours plus avec Security !", - "xpack.securitySolution.threatIntelligence.paywall.trial": "Démarrer un essai gratuit", - "xpack.securitySolution.threatIntelligence.paywall.upgrade": "Mettre à niveau", - "xpack.securitySolution.threatIntelligence.queryBar.filterOut": "Exclure", - "xpack.securitySolution.threatIntelligence.timeline.addToTimeline": "Ajouter à la chronologie", - "xpack.securitySolution.threatIntelligence.timeline.investigateInTimelineButtonIcon": "Investiguer dans la chronologie", - "xpack.securitySolution.threatIntelligence.updateStatus.updated": "Mis à jour", - "xpack.securitySolution.threatIntelligence.updateStatus.updating": "Mise à jour...", "xpack.timelines.clipboard.copied": "Copié", "xpack.timelines.clipboard.copy": "Copier", "xpack.timelines.clipboard.copy.successToastTitle": "Champ {field} copié dans le presse-papiers", diff --git a/x-pack/platform/plugins/private/translations/translations/ja-JP.json b/x-pack/platform/plugins/private/translations/translations/ja-JP.json index 9735ffb3ced2a..76131c1f50553 100644 --- a/x-pack/platform/plugins/private/translations/translations/ja-JP.json +++ b/x-pack/platform/plugins/private/translations/translations/ja-JP.json @@ -15990,13 +15990,6 @@ "xpack.dataVisualizer.file.simpleImportSettings.indexNameAriaLabel": "インデックス名、必須フィールド", "xpack.dataVisualizer.file.simpleImportSettings.indexNameFormRowLabel": "インデックス名", "xpack.dataVisualizer.file.simpleImportSettings.indexNamePlaceholder": "インデックス名", - "xpack.dataVisualizer.file.tikaTypes.excel": "Microsoft Office Excelドキュメント", - "xpack.dataVisualizer.file.tikaTypes.openDoc": "オープンドキュメント形式", - "xpack.dataVisualizer.file.tikaTypes.pdf": "PDF", - "xpack.dataVisualizer.file.tikaTypes.plainText": "プレインテキスト", - "xpack.dataVisualizer.file.tikaTypes.powerPoint": "Microsoft Office Power Pointドキュメント", - "xpack.dataVisualizer.file.tikaTypes.richText": "リッチテキスト形式", - "xpack.dataVisualizer.file.tikaTypes.word": "Microsoft Office Wordドキュメント", "xpack.dataVisualizer.file.uploadView.closeButton": "閉じる", "xpack.dataVisualizer.file.uploadView.importButton": "インポート", "xpack.dataVisualizer.file.uploadView.importingButton": "インポート中", @@ -41004,6 +40997,68 @@ "xpack.securitySolution.system.withResultDescription": "結果付き", "xpack.securitySolution.tables.rowItemHelper.moreDescription": "行は表示されていません", "xpack.securitySolution.tables.rowItemHelper.overflowButtonDescription": "他{count}件", + "xpack.securitySolution.threatIntelligence.addToBlockList": "ブロックリストエントリの追加", + "xpack.securitySolution.threatIntelligence.addToExistingCase": "既存のケースに追加", + "xpack.securitySolution.threatIntelligence.addToNewCase": "新しいケースに追加", + "xpack.securitySolution.threatIntelligence.blocklist.flyoutTitle": "ブロックリストの追加", + "xpack.securitySolution.threatIntelligence.cases.eventDescription": "侵害のインジケーターを追加しました", + "xpack.securitySolution.threatIntelligence.cases.indicatorFeedName": "フィード名", + "xpack.securitySolution.threatIntelligence.cases.indicatorName": "インジケーター名:", + "xpack.securitySolution.threatIntelligence.cases.indicatorType": "インジケータータイプ:", + "xpack.securitySolution.threatIntelligence.common.emptyPage.body1": "Elastic Threat Intelligenceでは、複数のソースのデータを一元的に集約することで、潜在的なセキュリティ脅威を簡単に分析、調査できます。", + "xpack.securitySolution.threatIntelligence.common.emptyPage.body2": "すべてのアクティブな脅威インテリジェンスフィードのデータを表示し、このページからアクションを実行できます。", + "xpack.securitySolution.threatIntelligence.common.emptyPage.body3": "Elastic Threat Intelligenceを開始するには、[統合]ページから1つ以上の脅威インテリジェンス統合を有効にするか、Filebeatを使用してデータを取り込みます。詳細については、{docsLink}をご覧ください。", + "xpack.securitySolution.threatIntelligence.common.emptyPage.buttonText": "統合の追加", + "xpack.securitySolution.threatIntelligence.common.emptyPage.docsLinkText": "セキュリティアプリドキュメント", + "xpack.securitySolution.threatIntelligence.common.emptyPage.imgAlt": "脅威インテリジェンス統合を有効にする", + "xpack.securitySolution.threatIntelligence.common.emptyPage.title": "Elastic Threat Intelligenceの基本操作", + "xpack.securitySolution.threatIntelligence.empty.description": "期間を長くして検索するか、検索を変更してください", + "xpack.securitySolution.threatIntelligence.empty.title": "検索条件と一致する結果がありません。", + "xpack.securitySolution.threatIntelligence.field.@timestamp": "@timestamp", + "xpack.securitySolution.threatIntelligence.field.threat.feed.name": "フィード", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.confidence": "信頼度", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.first_seen": "初回の認識", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.last_seen": "前回の認識", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.marking.tlp": "TLPマーキング", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.name": "インジケーター", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.type": "インジケータータイプ", + "xpack.securitySolution.threatIntelligence.indicator.barChart.popover": "さらにアクションを表示", + "xpack.securitySolution.threatIntelligence.indicator.barchartSection.title": "傾向", + "xpack.securitySolution.threatIntelligence.indicator.fieldSelector.label": "積み上げ", + "xpack.securitySolution.threatIntelligence.indicator.fieldsTable.fieldColumnLabel": "フィールド", + "xpack.securitySolution.threatIntelligence.indicator.fieldsTable.valueColumnLabel": "値", + "xpack.securitySolution.threatIntelligence.indicator.flyout.jsonTabLabel": "JSON", + "xpack.securitySolution.threatIntelligence.indicator.flyout.overviewTabLabel": "概要", + "xpack.securitySolution.threatIntelligence.indicator.flyout.panelSubTitle": "初回の表示:", + "xpack.securitySolution.threatIntelligence.indicator.flyout.panelTitleWithOverviewTab": "インジケーターの詳細", + "xpack.securitySolution.threatIntelligence.indicator.flyout.tableTabLabel": "表", + "xpack.securitySolution.threatIntelligence.indicator.flyoutOverviewTable.highlightedFields": "ハイライトされたフィールド", + "xpack.securitySolution.threatIntelligence.indicator.flyoutOverviewTable.viewAllFieldsInTable": "テーブルのすべてのフィールドを表示", + "xpack.securitySolution.threatIntelligence.indicator.flyoutTable.errorMessageBody": "インジケーターフィールドと値の表示エラーが発生しました。", + "xpack.securitySolution.threatIntelligence.indicator.flyoutTable.errorMessageTitle": "インジケーター情報を表示できませn", + "xpack.securitySolution.threatIntelligence.indicator.table.actionColumnLabel": "アクション", + "xpack.securitySolution.threatIntelligence.indicator.table.moreActions": "さらにアクションを表示", + "xpack.securitySolution.threatIntelligence.indicator.table.viewDetailsButton": "詳細を表示", + "xpack.securitySolution.threatIntelligence.indicatorNameFieldDescription": "実行時に生成されたインジケーター表示名", + "xpack.securitySolution.threatIntelligence.indicators.flyout.take-action.button": "アクションを実行", + "xpack.securitySolution.threatIntelligence.indicators.table.copyToClipboardLabel": "クリップボードにコピー", + "xpack.securitySolution.threatIntelligence.inspectorFlyoutTitle": "インジケーター検索リクエスト", + "xpack.securitySolution.threatIntelligence.inspectTitle": "検査", + "xpack.securitySolution.threatIntelligence.investigateInTimelineButton": "タイムラインで調査", + "xpack.securitySolution.threatIntelligence.more-actions.popover": "さらにアクションを表示", + "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemDescription": "Elastic threat intelligenceでは、最新の脅威または過去の確認済みの脅威にさらされ、脆弱であるかどうかを確認できます。", + "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemKeywords": "インジケーター", + "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemLabel": "インジケーター", + "xpack.securitySolution.threatIntelligence.navigation.intelligenceNavItemLabel": "インテリジェンス", + "xpack.securitySolution.threatIntelligence.paywall.body": "脅威インテリジェンスを使用するには、無料トライアルを開始するか、ライセンスをEnterpriseにアップグレードしてください。", + "xpack.securitySolution.threatIntelligence.paywall.title": "Securityではさまざまなことが可能です!", + "xpack.securitySolution.threatIntelligence.paywall.trial": "無料トライアルをはじめる", + "xpack.securitySolution.threatIntelligence.paywall.upgrade": "アップグレード", + "xpack.securitySolution.threatIntelligence.queryBar.filterOut": "除外", + "xpack.securitySolution.threatIntelligence.timeline.addToTimeline": "タイムラインに追加", + "xpack.securitySolution.threatIntelligence.timeline.investigateInTimelineButtonIcon": "タイムラインで調査", + "xpack.securitySolution.threatIntelligence.updateStatus.updated": "更新しました", + "xpack.securitySolution.threatIntelligence.updateStatus.updating": "更新中...", "xpack.securitySolution.threatMatch.andDescription": "AND", "xpack.securitySolution.threatMatch.fieldDescription": "フィールド", "xpack.securitySolution.threatMatch.fieldPlaceholderDescription": "検索", @@ -46059,68 +46114,6 @@ "xpack.synthetics.windowValueExpression.numberOfChecksPopoverTitleLabel": "チェックの数", "xpack.synthetics.windowValueExpression.numberOfLocPopoverTitleLabel": "場所数", "xpack.synthetics.windowValueExpression.percentLabel": "{numberOfLocations} {numberOfLocations, plural, other {個の場所}}", - "xpack.securitySolution.threatIntelligence.addToBlockList": "ブロックリストエントリの追加", - "xpack.securitySolution.threatIntelligence.addToExistingCase": "既存のケースに追加", - "xpack.securitySolution.threatIntelligence.addToNewCase": "新しいケースに追加", - "xpack.securitySolution.threatIntelligence.blocklist.flyoutTitle": "ブロックリストの追加", - "xpack.securitySolution.threatIntelligence.cases.eventDescription": "侵害のインジケーターを追加しました", - "xpack.securitySolution.threatIntelligence.cases.indicatorFeedName": "フィード名", - "xpack.securitySolution.threatIntelligence.cases.indicatorName": "インジケーター名:", - "xpack.securitySolution.threatIntelligence.cases.indicatorType": "インジケータータイプ:", - "xpack.securitySolution.threatIntelligence.common.emptyPage.body1": "Elastic Threat Intelligenceでは、複数のソースのデータを一元的に集約することで、潜在的なセキュリティ脅威を簡単に分析、調査できます。", - "xpack.securitySolution.threatIntelligence.common.emptyPage.body2": "すべてのアクティブな脅威インテリジェンスフィードのデータを表示し、このページからアクションを実行できます。", - "xpack.securitySolution.threatIntelligence.common.emptyPage.body3": "Elastic Threat Intelligenceを開始するには、[統合]ページから1つ以上の脅威インテリジェンス統合を有効にするか、Filebeatを使用してデータを取り込みます。詳細については、{docsLink}をご覧ください。", - "xpack.securitySolution.threatIntelligence.common.emptyPage.buttonText": "統合の追加", - "xpack.securitySolution.threatIntelligence.common.emptyPage.docsLinkText": "セキュリティアプリドキュメント", - "xpack.securitySolution.threatIntelligence.common.emptyPage.imgAlt": "脅威インテリジェンス統合を有効にする", - "xpack.securitySolution.threatIntelligence.common.emptyPage.title": "Elastic Threat Intelligenceの基本操作", - "xpack.securitySolution.threatIntelligence.empty.description": "期間を長くして検索するか、検索を変更してください", - "xpack.securitySolution.threatIntelligence.empty.title": "検索条件と一致する結果がありません。", - "xpack.securitySolution.threatIntelligence.field.@timestamp": "@timestamp", - "xpack.securitySolution.threatIntelligence.field.threat.feed.name": "フィード", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.confidence": "信頼度", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.first_seen": "初回の認識", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.last_seen": "前回の認識", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.marking.tlp": "TLPマーキング", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.name": "インジケーター", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.type": "インジケータータイプ", - "xpack.securitySolution.threatIntelligence.indicator.barChart.popover": "さらにアクションを表示", - "xpack.securitySolution.threatIntelligence.indicator.barchartSection.title": "傾向", - "xpack.securitySolution.threatIntelligence.indicator.fieldSelector.label": "積み上げ", - "xpack.securitySolution.threatIntelligence.indicator.fieldsTable.fieldColumnLabel": "フィールド", - "xpack.securitySolution.threatIntelligence.indicator.fieldsTable.valueColumnLabel": "値", - "xpack.securitySolution.threatIntelligence.indicator.flyout.jsonTabLabel": "JSON", - "xpack.securitySolution.threatIntelligence.indicator.flyout.overviewTabLabel": "概要", - "xpack.securitySolution.threatIntelligence.indicator.flyout.panelSubTitle": "初回の表示:", - "xpack.securitySolution.threatIntelligence.indicator.flyout.panelTitleWithOverviewTab": "インジケーターの詳細", - "xpack.securitySolution.threatIntelligence.indicator.flyout.tableTabLabel": "表", - "xpack.securitySolution.threatIntelligence.indicator.flyoutOverviewTable.highlightedFields": "ハイライトされたフィールド", - "xpack.securitySolution.threatIntelligence.indicator.flyoutOverviewTable.viewAllFieldsInTable": "テーブルのすべてのフィールドを表示", - "xpack.securitySolution.threatIntelligence.indicator.flyoutTable.errorMessageBody": "インジケーターフィールドと値の表示エラーが発生しました。", - "xpack.securitySolution.threatIntelligence.indicator.flyoutTable.errorMessageTitle": "インジケーター情報を表示できませn", - "xpack.securitySolution.threatIntelligence.indicator.table.actionColumnLabel": "アクション", - "xpack.securitySolution.threatIntelligence.indicator.table.moreActions": "さらにアクションを表示", - "xpack.securitySolution.threatIntelligence.indicator.table.viewDetailsButton": "詳細を表示", - "xpack.securitySolution.threatIntelligence.indicatorNameFieldDescription": "実行時に生成されたインジケーター表示名", - "xpack.securitySolution.threatIntelligence.indicators.flyout.take-action.button": "アクションを実行", - "xpack.securitySolution.threatIntelligence.indicators.table.copyToClipboardLabel": "クリップボードにコピー", - "xpack.securitySolution.threatIntelligence.inspectorFlyoutTitle": "インジケーター検索リクエスト", - "xpack.securitySolution.threatIntelligence.inspectTitle": "検査", - "xpack.securitySolution.threatIntelligence.investigateInTimelineButton": "タイムラインで調査", - "xpack.securitySolution.threatIntelligence.more-actions.popover": "さらにアクションを表示", - "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemDescription": "Elastic threat intelligenceでは、最新の脅威または過去の確認済みの脅威にさらされ、脆弱であるかどうかを確認できます。", - "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemKeywords": "インジケーター", - "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemLabel": "インジケーター", - "xpack.securitySolution.threatIntelligence.navigation.intelligenceNavItemLabel": "インテリジェンス", - "xpack.securitySolution.threatIntelligence.paywall.body": "脅威インテリジェンスを使用するには、無料トライアルを開始するか、ライセンスをEnterpriseにアップグレードしてください。", - "xpack.securitySolution.threatIntelligence.paywall.title": "Securityではさまざまなことが可能です!", - "xpack.securitySolution.threatIntelligence.paywall.trial": "無料トライアルをはじめる", - "xpack.securitySolution.threatIntelligence.paywall.upgrade": "アップグレード", - "xpack.securitySolution.threatIntelligence.queryBar.filterOut": "除外", - "xpack.securitySolution.threatIntelligence.timeline.addToTimeline": "タイムラインに追加", - "xpack.securitySolution.threatIntelligence.timeline.investigateInTimelineButtonIcon": "タイムラインで調査", - "xpack.securitySolution.threatIntelligence.updateStatus.updated": "更新しました", - "xpack.securitySolution.threatIntelligence.updateStatus.updating": "更新中...", "xpack.timelines.clipboard.copied": "コピー完了", "xpack.timelines.clipboard.copy": "コピー", "xpack.timelines.clipboard.copy.successToastTitle": "フィールド{field}をクリップボードにコピーしました", diff --git a/x-pack/platform/plugins/private/translations/translations/zh-CN.json b/x-pack/platform/plugins/private/translations/translations/zh-CN.json index 9ff32fff5a6c3..c77df92c4b495 100644 --- a/x-pack/platform/plugins/private/translations/translations/zh-CN.json +++ b/x-pack/platform/plugins/private/translations/translations/zh-CN.json @@ -16026,13 +16026,6 @@ "xpack.dataVisualizer.file.simpleImportSettings.indexNameAriaLabel": "索引名称,必填字段", "xpack.dataVisualizer.file.simpleImportSettings.indexNameFormRowLabel": "索引名称", "xpack.dataVisualizer.file.simpleImportSettings.indexNamePlaceholder": "索引名称", - "xpack.dataVisualizer.file.tikaTypes.excel": "Microsoft Office Excel 文档", - "xpack.dataVisualizer.file.tikaTypes.openDoc": "开放式文档格式", - "xpack.dataVisualizer.file.tikaTypes.pdf": "PDF", - "xpack.dataVisualizer.file.tikaTypes.plainText": "纯文本", - "xpack.dataVisualizer.file.tikaTypes.powerPoint": "Microsoft Office PowerPoint 文档", - "xpack.dataVisualizer.file.tikaTypes.richText": "富文本格式", - "xpack.dataVisualizer.file.tikaTypes.word": "Microsoft Office Word 文档", "xpack.dataVisualizer.file.uploadView.closeButton": "关闭", "xpack.dataVisualizer.file.uploadView.importButton": "导入", "xpack.dataVisualizer.file.uploadView.importingButton": "正在导入", @@ -41081,6 +41074,68 @@ "xpack.securitySolution.system.withResultDescription": ",结果为", "xpack.securitySolution.tables.rowItemHelper.moreDescription": "未显示", "xpack.securitySolution.tables.rowItemHelper.overflowButtonDescription": "另外 {count} 个", + "xpack.securitySolution.threatIntelligence.addToBlockList": "添加阻止列表条目", + "xpack.securitySolution.threatIntelligence.addToExistingCase": "添加到现有案例", + "xpack.securitySolution.threatIntelligence.addToNewCase": "添加到新案例", + "xpack.securitySolution.threatIntelligence.blocklist.flyoutTitle": "添加阻止列表", + "xpack.securitySolution.threatIntelligence.cases.eventDescription": "已添加受损指标", + "xpack.securitySolution.threatIntelligence.cases.indicatorFeedName": "源名称:", + "xpack.securitySolution.threatIntelligence.cases.indicatorName": "指标名称:", + "xpack.securitySolution.threatIntelligence.cases.indicatorType": "指标类型:", + "xpack.securitySolution.threatIntelligence.common.emptyPage.body1": "利用 Elastic 威胁情报,可以通过将多个来源的数据聚合到一个位置,轻松分析和调查潜在的安全威胁。", + "xpack.securitySolution.threatIntelligence.common.emptyPage.body2": "您将可以查看来自所有已激活威胁情报馈送的数据,并从此页面执行操作。", + "xpack.securitySolution.threatIntelligence.common.emptyPage.body3": "要开始使用 Elastic 威胁情报,请从“集成”页面启用一个或多个威胁情报集成,或使用 Filebeat 采集数据。有关更多信息,请查看 {docsLink}。", + "xpack.securitySolution.threatIntelligence.common.emptyPage.buttonText": "添加集成", + "xpack.securitySolution.threatIntelligence.common.emptyPage.docsLinkText": "Security 应用文档", + "xpack.securitySolution.threatIntelligence.common.emptyPage.imgAlt": "启用威胁情报集成", + "xpack.securitySolution.threatIntelligence.common.emptyPage.title": "Elastic 威胁情报入门", + "xpack.securitySolution.threatIntelligence.empty.description": "尝试搜索更长的时间段或修改您的搜索", + "xpack.securitySolution.threatIntelligence.empty.title": "没有任何结果匹配您的搜索条件", + "xpack.securitySolution.threatIntelligence.field.@timestamp": "@timestamp", + "xpack.securitySolution.threatIntelligence.field.threat.feed.name": "馈送", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.confidence": "置信度", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.first_seen": "首次看到时间", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.last_seen": "最后看到时间", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.marking.tlp": "TLP 标记", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.name": "指标", + "xpack.securitySolution.threatIntelligence.field.threat.indicator.type": "指标类型", + "xpack.securitySolution.threatIntelligence.indicator.barChart.popover": "更多操作", + "xpack.securitySolution.threatIntelligence.indicator.barchartSection.title": "趋势", + "xpack.securitySolution.threatIntelligence.indicator.fieldSelector.label": "堆叠依据", + "xpack.securitySolution.threatIntelligence.indicator.fieldsTable.fieldColumnLabel": "字段", + "xpack.securitySolution.threatIntelligence.indicator.fieldsTable.valueColumnLabel": "值", + "xpack.securitySolution.threatIntelligence.indicator.flyout.jsonTabLabel": "JSON", + "xpack.securitySolution.threatIntelligence.indicator.flyout.overviewTabLabel": "概览", + "xpack.securitySolution.threatIntelligence.indicator.flyout.panelSubTitle": "首次看到时间:", + "xpack.securitySolution.threatIntelligence.indicator.flyout.panelTitleWithOverviewTab": "指标详情", + "xpack.securitySolution.threatIntelligence.indicator.flyout.tableTabLabel": "表", + "xpack.securitySolution.threatIntelligence.indicator.flyoutOverviewTable.highlightedFields": "突出显示的字段", + "xpack.securitySolution.threatIntelligence.indicator.flyoutOverviewTable.viewAllFieldsInTable": "查看表中的所有字段", + "xpack.securitySolution.threatIntelligence.indicator.flyoutTable.errorMessageBody": "显示指标字段和值时出现错误。", + "xpack.securitySolution.threatIntelligence.indicator.flyoutTable.errorMessageTitle": "无法显示指标信息", + "xpack.securitySolution.threatIntelligence.indicator.table.actionColumnLabel": "操作", + "xpack.securitySolution.threatIntelligence.indicator.table.moreActions": "更多操作", + "xpack.securitySolution.threatIntelligence.indicator.table.viewDetailsButton": "查看详情", + "xpack.securitySolution.threatIntelligence.indicatorNameFieldDescription": "在运行时中生成的指标显示名称", + "xpack.securitySolution.threatIntelligence.indicators.flyout.take-action.button": "采取操作", + "xpack.securitySolution.threatIntelligence.indicators.table.copyToClipboardLabel": "复制到剪贴板", + "xpack.securitySolution.threatIntelligence.inspectorFlyoutTitle": "指标搜索请求", + "xpack.securitySolution.threatIntelligence.inspectTitle": "检查", + "xpack.securitySolution.threatIntelligence.investigateInTimelineButton": "在时间线中调查", + "xpack.securitySolution.threatIntelligence.more-actions.popover": "更多操作", + "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemDescription": "Elastic 威胁情报有助于您了解您是否易于受到或已面临当前或历史上的已知威胁。", + "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemKeywords": "指标", + "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemLabel": "指标", + "xpack.securitySolution.threatIntelligence.navigation.intelligenceNavItemLabel": "情报", + "xpack.securitySolution.threatIntelligence.paywall.body": "开始免费试用或将许可证升级到企业版以使用威胁情报。", + "xpack.securitySolution.threatIntelligence.paywall.title": "Security 让您事半功倍!", + "xpack.securitySolution.threatIntelligence.paywall.trial": "开始免费试用", + "xpack.securitySolution.threatIntelligence.paywall.upgrade": "升级", + "xpack.securitySolution.threatIntelligence.queryBar.filterOut": "筛除", + "xpack.securitySolution.threatIntelligence.timeline.addToTimeline": "添加到时间线", + "xpack.securitySolution.threatIntelligence.timeline.investigateInTimelineButtonIcon": "在时间线中调查", + "xpack.securitySolution.threatIntelligence.updateStatus.updated": "已更新", + "xpack.securitySolution.threatIntelligence.updateStatus.updating": "正在更新......", "xpack.securitySolution.threatMatch.andDescription": "AND", "xpack.securitySolution.threatMatch.fieldDescription": "字段", "xpack.securitySolution.threatMatch.fieldPlaceholderDescription": "搜索", @@ -46144,68 +46199,6 @@ "xpack.synthetics.windowValueExpression.numberOfChecksPopoverTitleLabel": "检查数目", "xpack.synthetics.windowValueExpression.numberOfLocPopoverTitleLabel": "位置数", "xpack.synthetics.windowValueExpression.percentLabel": "{numberOfLocations} 个{numberOfLocations, plural, other {位置}}", - "xpack.securitySolution.threatIntelligence.addToBlockList": "添加阻止列表条目", - "xpack.securitySolution.threatIntelligence.addToExistingCase": "添加到现有案例", - "xpack.securitySolution.threatIntelligence.addToNewCase": "添加到新案例", - "xpack.securitySolution.threatIntelligence.blocklist.flyoutTitle": "添加阻止列表", - "xpack.securitySolution.threatIntelligence.cases.eventDescription": "已添加受损指标", - "xpack.securitySolution.threatIntelligence.cases.indicatorFeedName": "源名称:", - "xpack.securitySolution.threatIntelligence.cases.indicatorName": "指标名称:", - "xpack.securitySolution.threatIntelligence.cases.indicatorType": "指标类型:", - "xpack.securitySolution.threatIntelligence.common.emptyPage.body1": "利用 Elastic 威胁情报,可以通过将多个来源的数据聚合到一个位置,轻松分析和调查潜在的安全威胁。", - "xpack.securitySolution.threatIntelligence.common.emptyPage.body2": "您将可以查看来自所有已激活威胁情报馈送的数据,并从此页面执行操作。", - "xpack.securitySolution.threatIntelligence.common.emptyPage.body3": "要开始使用 Elastic 威胁情报,请从“集成”页面启用一个或多个威胁情报集成,或使用 Filebeat 采集数据。有关更多信息,请查看 {docsLink}。", - "xpack.securitySolution.threatIntelligence.common.emptyPage.buttonText": "添加集成", - "xpack.securitySolution.threatIntelligence.common.emptyPage.docsLinkText": "Security 应用文档", - "xpack.securitySolution.threatIntelligence.common.emptyPage.imgAlt": "启用威胁情报集成", - "xpack.securitySolution.threatIntelligence.common.emptyPage.title": "Elastic 威胁情报入门", - "xpack.securitySolution.threatIntelligence.empty.description": "尝试搜索更长的时间段或修改您的搜索", - "xpack.securitySolution.threatIntelligence.empty.title": "没有任何结果匹配您的搜索条件", - "xpack.securitySolution.threatIntelligence.field.@timestamp": "@timestamp", - "xpack.securitySolution.threatIntelligence.field.threat.feed.name": "馈送", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.confidence": "置信度", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.first_seen": "首次看到时间", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.last_seen": "最后看到时间", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.marking.tlp": "TLP 标记", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.name": "指标", - "xpack.securitySolution.threatIntelligence.field.threat.indicator.type": "指标类型", - "xpack.securitySolution.threatIntelligence.indicator.barChart.popover": "更多操作", - "xpack.securitySolution.threatIntelligence.indicator.barchartSection.title": "趋势", - "xpack.securitySolution.threatIntelligence.indicator.fieldSelector.label": "堆叠依据", - "xpack.securitySolution.threatIntelligence.indicator.fieldsTable.fieldColumnLabel": "字段", - "xpack.securitySolution.threatIntelligence.indicator.fieldsTable.valueColumnLabel": "值", - "xpack.securitySolution.threatIntelligence.indicator.flyout.jsonTabLabel": "JSON", - "xpack.securitySolution.threatIntelligence.indicator.flyout.overviewTabLabel": "概览", - "xpack.securitySolution.threatIntelligence.indicator.flyout.panelSubTitle": "首次看到时间:", - "xpack.securitySolution.threatIntelligence.indicator.flyout.panelTitleWithOverviewTab": "指标详情", - "xpack.securitySolution.threatIntelligence.indicator.flyout.tableTabLabel": "表", - "xpack.securitySolution.threatIntelligence.indicator.flyoutOverviewTable.highlightedFields": "突出显示的字段", - "xpack.securitySolution.threatIntelligence.indicator.flyoutOverviewTable.viewAllFieldsInTable": "查看表中的所有字段", - "xpack.securitySolution.threatIntelligence.indicator.flyoutTable.errorMessageBody": "显示指标字段和值时出现错误。", - "xpack.securitySolution.threatIntelligence.indicator.flyoutTable.errorMessageTitle": "无法显示指标信息", - "xpack.securitySolution.threatIntelligence.indicator.table.actionColumnLabel": "操作", - "xpack.securitySolution.threatIntelligence.indicator.table.moreActions": "更多操作", - "xpack.securitySolution.threatIntelligence.indicator.table.viewDetailsButton": "查看详情", - "xpack.securitySolution.threatIntelligence.indicatorNameFieldDescription": "在运行时中生成的指标显示名称", - "xpack.securitySolution.threatIntelligence.indicators.flyout.take-action.button": "采取操作", - "xpack.securitySolution.threatIntelligence.indicators.table.copyToClipboardLabel": "复制到剪贴板", - "xpack.securitySolution.threatIntelligence.inspectorFlyoutTitle": "指标搜索请求", - "xpack.securitySolution.threatIntelligence.inspectTitle": "检查", - "xpack.securitySolution.threatIntelligence.investigateInTimelineButton": "在时间线中调查", - "xpack.securitySolution.threatIntelligence.more-actions.popover": "更多操作", - "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemDescription": "Elastic 威胁情报有助于您了解您是否易于受到或已面临当前或历史上的已知威胁。", - "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemKeywords": "指标", - "xpack.securitySolution.threatIntelligence.navigation.indicatorsNavItemLabel": "指标", - "xpack.securitySolution.threatIntelligence.navigation.intelligenceNavItemLabel": "情报", - "xpack.securitySolution.threatIntelligence.paywall.body": "开始免费试用或将许可证升级到企业版以使用威胁情报。", - "xpack.securitySolution.threatIntelligence.paywall.title": "Security 让您事半功倍!", - "xpack.securitySolution.threatIntelligence.paywall.trial": "开始免费试用", - "xpack.securitySolution.threatIntelligence.paywall.upgrade": "升级", - "xpack.securitySolution.threatIntelligence.queryBar.filterOut": "筛除", - "xpack.securitySolution.threatIntelligence.timeline.addToTimeline": "添加到时间线", - "xpack.securitySolution.threatIntelligence.timeline.investigateInTimelineButtonIcon": "在时间线中调查", - "xpack.securitySolution.threatIntelligence.updateStatus.updated": "已更新", - "xpack.securitySolution.threatIntelligence.updateStatus.updating": "正在更新......", "xpack.timelines.clipboard.copied": "已复制", "xpack.timelines.clipboard.copy": "复制", "xpack.timelines.clipboard.copy.successToastTitle": "已将字段 {field} 复制到剪贴板", From e39ef43676a6b92768d1e07130e6b54a2a78712d Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 5 Jun 2025 12:35:15 +0100 Subject: [PATCH 15/16] making BehaviourSubjects private --- .../file_upload_manager/file_manager.ts | 30 ++++++++++++++----- .../shared/file-upload/src/use_file_upload.ts | 16 ++++------ 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_manager.ts b/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_manager.ts index 7edff9bd43b76..c1b99ca9abf4d 100644 --- a/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_manager.ts +++ b/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_manager.ts @@ -88,22 +88,22 @@ export class FileUploadManager { private readonly existingIndexMappings$ = new BehaviorSubject(null); private mappingsCheckSubscription: Subscription; - public readonly settings$ = new BehaviorSubject>({ + private readonly settings$ = new BehaviorSubject>({ json: {}, valid: false, }); - public readonly mappings$ = new BehaviorSubject>({ + private readonly mappings$ = new BehaviorSubject>({ json: {}, valid: false, }); - public readonly existingIndexName$ = new BehaviorSubject(null); + private readonly existingIndexName$ = new BehaviorSubject(null); private inferenceId: string | null = null; private importer: IImporter | null = null; private timeFieldName: string | undefined | null = null; private commonFileFormat: string | null = null; - public readonly uploadStatus$ = new BehaviorSubject({ + private readonly uploadStatus$ = new BehaviorSubject({ analysisStatus: STATUS.NOT_STARTED, overallImportStatus: STATUS.NOT_STARTED, indexCreated: STATUS.NOT_STARTED, @@ -274,6 +274,16 @@ export class FileUploadManager { }; } + public getUploadStatus$() { + return this.uploadStatus$.asObservable(); + } + public getUploadStatus() { + return this.uploadStatus$.getValue(); + } + + public getExistingIndexName$() { + return this.existingIndexName$.asObservable(); + } public getExistingIndexName() { return this.existingIndexName$.getValue(); } @@ -339,14 +349,20 @@ export class FileUploadManager { }); } + public getMappings$() { + return this.mappings$.asObservable(); + } public getMappings() { - return this.mappings$.getValue().json; + return this.mappings$.getValue(); } public updateMappings(mappings: MappingTypeMapping | string) { this.updateSettingsOrMappings('mappings', mappings); } + public getSettings$() { + return this.settings$.asObservable(); + } public getSettings() { return this.settings$.getValue(); } @@ -400,7 +416,7 @@ export class FileUploadManager { indexName: string, dataViewName?: string | null ): Promise { - const mappings = this.getMappings(); + const mappings = this.getMappings().json; const pipelines = this.getPipelines(); if (mappings === null || pipelines === null || this.commonFileFormat === null) { @@ -626,7 +642,7 @@ export class FileUploadManager { } private addSemanticTextField() { - const mappings = this.getMappings(); + const mappings = this.getMappings().json; const pipelines = this.getPipelines(); if ( this.isTikaFormat() && diff --git a/x-pack/platform/packages/shared/file-upload/src/use_file_upload.ts b/x-pack/platform/packages/shared/file-upload/src/use_file_upload.ts index aef7e2f8779c1..c0880988921f4 100644 --- a/x-pack/platform/packages/shared/file-upload/src/use_file_upload.ts +++ b/x-pack/platform/packages/shared/file-upload/src/use_file_upload.ts @@ -58,8 +58,8 @@ export function useFileUpload( const filesStatus = useObservable(fileUploadManager.fileAnalysisStatus$, []); const uploadStatus = useObservable( - fileUploadManager.uploadStatus$, - fileUploadManager.uploadStatus$.getValue() + fileUploadManager.getUploadStatus$(), + fileUploadManager.getUploadStatus() ); const fileClashes = useMemo( () => uploadStatus.fileClashes.some((f) => f.clash === CLASH_ERROR_TYPE.ERROR), @@ -143,7 +143,7 @@ export function useFileUpload( ]); const existingIndexName = useObservable( - fileUploadManager.existingIndexName$, + fileUploadManager.getExistingIndexName$(), fileUploadManager.getExistingIndexName() ); @@ -168,14 +168,8 @@ export function useFileUpload( existingIndexName, ]); - const mappings = useObservable( - fileUploadManager.mappings$, - fileUploadManager.mappings$.getValue() - ); - const settings = useObservable( - fileUploadManager.settings$, - fileUploadManager.settings$.getValue() - ); + const mappings = useObservable(fileUploadManager.getMappings$(), fileUploadManager.getMappings()); + const settings = useObservable(fileUploadManager.getSettings$(), fileUploadManager.getSettings()); const setExistingIndexName = useCallback( (idxName: string | null) => { From 08c98770d50bca1f7c9fb0c6ee949183e983e446 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 6 Jun 2025 08:45:12 +0100 Subject: [PATCH 16/16] correcting BehaviorSubject visibility --- .../file_upload_manager/file_manager.ts | 53 +++++++++---------- .../shared/file-upload/src/use_file_upload.ts | 8 +-- .../public/lite/file_status.tsx | 2 +- 3 files changed, 29 insertions(+), 34 deletions(-) diff --git a/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_manager.ts b/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_manager.ts index c1b99ca9abf4d..159676b01e9e3 100644 --- a/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_manager.ts +++ b/x-pack/platform/packages/shared/file-upload/file_upload_manager/file_manager.ts @@ -88,22 +88,27 @@ export class FileUploadManager { private readonly existingIndexMappings$ = new BehaviorSubject(null); private mappingsCheckSubscription: Subscription; - private readonly settings$ = new BehaviorSubject>({ + private readonly _settings$ = new BehaviorSubject>({ json: {}, valid: false, }); - private readonly mappings$ = new BehaviorSubject>({ + public readonly settings$ = this._settings$.asObservable(); + + private readonly _mappings$ = new BehaviorSubject>({ json: {}, valid: false, }); - private readonly existingIndexName$ = new BehaviorSubject(null); + public readonly mappings$ = this._mappings$.asObservable(); + + private readonly _existingIndexName$ = new BehaviorSubject(null); + public readonly existingIndexName$ = this._existingIndexName$.asObservable(); private inferenceId: string | null = null; private importer: IImporter | null = null; private timeFieldName: string | undefined | null = null; private commonFileFormat: string | null = null; - private readonly uploadStatus$ = new BehaviorSubject({ + private readonly _uploadStatus$ = new BehaviorSubject({ analysisStatus: STATUS.NOT_STARTED, overallImportStatus: STATUS.NOT_STARTED, indexCreated: STATUS.NOT_STARTED, @@ -120,6 +125,8 @@ export class FileUploadManager { pipelinesJsonValid: true, errors: [], }); + public readonly uploadStatus$ = this._uploadStatus$.asObservable(); + private autoAddSemanticTextField: boolean = false; constructor( @@ -152,7 +159,7 @@ export class FileUploadManager { }); this.analysisValid$.next(true); - const uploadStatus = this.uploadStatus$.getValue(); + const uploadStatus = this._uploadStatus$.getValue(); if (uploadStatus.overallImportStatus === STATUS.STARTED) { return; } @@ -200,15 +207,15 @@ export class FileUploadManager { destroy() { this.files$.complete(); this.analysisValid$.complete(); - this.settings$.complete(); - this.mappings$.complete(); + this._settings$.complete(); + this._mappings$.complete(); this.existingIndexMappings$.complete(); - this.uploadStatus$.complete(); + this._uploadStatus$.complete(); this.mappingsCheckSubscription.unsubscribe(); } private setStatus(status: Partial) { - this.uploadStatus$.next({ - ...this.uploadStatus$.getValue(), + this._uploadStatus$.next({ + ...this._uploadStatus$.getValue(), ...status, }); } @@ -245,7 +252,7 @@ export class FileUploadManager { } public async removeClashingFiles() { - const fileClashes = this.uploadStatus$.getValue().fileClashes; + const fileClashes = this._uploadStatus$.getValue().fileClashes; const filesToDestroy: FileWrapper[] = []; const files = this.getFiles(); const newFiles = files.filter((file, i) => { @@ -274,24 +281,18 @@ export class FileUploadManager { }; } - public getUploadStatus$() { - return this.uploadStatus$.asObservable(); - } public getUploadStatus() { - return this.uploadStatus$.getValue(); + return this._uploadStatus$.getValue(); } - public getExistingIndexName$() { - return this.existingIndexName$.asObservable(); - } public getExistingIndexName() { - return this.existingIndexName$.getValue(); + return this._existingIndexName$.getValue(); } public setExistingIndexName(name: string | null) { this.setStatus({ analysisStatus: STATUS.NOT_STARTED, }); - this.existingIndexName$.next(name); + this._existingIndexName$.next(name); if (name === null) { this.existingIndexMappings$.next(null); } else { @@ -349,22 +350,16 @@ export class FileUploadManager { }); } - public getMappings$() { - return this.mappings$.asObservable(); - } public getMappings() { - return this.mappings$.getValue(); + return this._mappings$.getValue(); } public updateMappings(mappings: MappingTypeMapping | string) { this.updateSettingsOrMappings('mappings', mappings); } - public getSettings$() { - return this.settings$.asObservable(); - } public getSettings() { - return this.settings$.getValue(); + return this._settings$.getValue(); } public updateSettings(settings: IndicesIndexSettings | string) { @@ -375,7 +370,7 @@ export class FileUploadManager { mode: 'settings' | 'mappings', config: IndicesIndexSettings | MappingTypeMapping | string ) { - const config$ = mode === 'settings' ? this.settings$ : this.mappings$; + const config$ = mode === 'settings' ? this._settings$ : this._mappings$; const jsonValidKey = mode === 'settings' ? 'settingsJsonValid' : 'mappingsJsonValid'; const currentConfig = config$.getValue(); if (typeof config === 'string') { diff --git a/x-pack/platform/packages/shared/file-upload/src/use_file_upload.ts b/x-pack/platform/packages/shared/file-upload/src/use_file_upload.ts index c0880988921f4..fe4bf26126f28 100644 --- a/x-pack/platform/packages/shared/file-upload/src/use_file_upload.ts +++ b/x-pack/platform/packages/shared/file-upload/src/use_file_upload.ts @@ -58,7 +58,7 @@ export function useFileUpload( const filesStatus = useObservable(fileUploadManager.fileAnalysisStatus$, []); const uploadStatus = useObservable( - fileUploadManager.getUploadStatus$(), + fileUploadManager.uploadStatus$, fileUploadManager.getUploadStatus() ); const fileClashes = useMemo( @@ -143,7 +143,7 @@ export function useFileUpload( ]); const existingIndexName = useObservable( - fileUploadManager.getExistingIndexName$(), + fileUploadManager.existingIndexName$, fileUploadManager.getExistingIndexName() ); @@ -168,8 +168,8 @@ export function useFileUpload( existingIndexName, ]); - const mappings = useObservable(fileUploadManager.getMappings$(), fileUploadManager.getMappings()); - const settings = useObservable(fileUploadManager.getSettings$(), fileUploadManager.getSettings()); + const mappings = useObservable(fileUploadManager.mappings$, fileUploadManager.getMappings()); + const settings = useObservable(fileUploadManager.settings$, fileUploadManager.getSettings()); const setExistingIndexName = useCallback( (idxName: string | null) => { diff --git a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_status.tsx b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_status.tsx index 82bd5f2045805..d87369de78f43 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/lite/file_status.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/lite/file_status.tsx @@ -94,7 +94,7 @@ export const FileStatus: FC = ({ {fileStatus.fileName}  - {fileStatus.fileSizeInfo.fileSize} + {fileStatus.fileSizeInfo.fileSizeFormatted}