diff --git a/x-pack/plugins/ingest_manager/common/types/models/package_config.ts b/x-pack/plugins/ingest_manager/common/types/models/package_config.ts index e9595bab0174..0ff56e6d05d3 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/package_config.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/package_config.ts @@ -55,9 +55,14 @@ export interface NewPackageConfig { inputs: NewPackageConfigInput[]; } +export interface UpdatePackageConfig extends NewPackageConfig { + version?: string; +} + export interface PackageConfig extends Omit { id: string; inputs: PackageConfigInput[]; + version?: string; revision: number; updated_at: string; updated_by: string; @@ -65,4 +70,4 @@ export interface PackageConfig extends Omit { created_by: string; } -export type PackageConfigSOAttributes = Omit; +export type PackageConfigSOAttributes = Omit; diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/package_config.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/package_config.ts index 4b8abbde47d5..e62645debb74 100644 --- a/x-pack/plugins/ingest_manager/common/types/rest_spec/package_config.ts +++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/package_config.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { PackageConfig, NewPackageConfig } from '../models'; +import { PackageConfig, NewPackageConfig, UpdatePackageConfig } from '../models'; export interface GetPackageConfigsRequest { query: { @@ -42,7 +42,7 @@ export interface CreatePackageConfigResponse { } export type UpdatePackageConfigRequest = GetOnePackageConfigRequest & { - body: NewPackageConfig; + body: UpdatePackageConfig; }; export type UpdatePackageConfigResponse = CreatePackageConfigResponse; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/use_request.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/use_request.ts index fbbc482fb96a..1486c2e50b7a 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/use_request.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/use_request.ts @@ -17,33 +17,39 @@ let httpClient: HttpSetup; export type UseRequestConfig = _UseRequestConfig; +interface RequestError extends Error { + statusCode?: number; +} + export const setHttpClient = (client: HttpSetup) => { httpClient = client; }; -export const sendRequest = ( +export const sendRequest = ( config: SendRequestConfig -): Promise> => { +): Promise> => { if (!httpClient) { throw new Error('sendRequest has no http client set'); } - return _sendRequest(httpClient, config); + return _sendRequest(httpClient, config); }; -export const useRequest = (config: UseRequestConfig) => { +export const useRequest = (config: UseRequestConfig) => { if (!httpClient) { throw new Error('sendRequest has no http client set'); } - return _useRequest(httpClient, config); + return _useRequest(httpClient, config); }; export type SendConditionalRequestConfig = | (SendRequestConfig & { shouldSendRequest: true }) | (Partial & { shouldSendRequest: false }); -export const useConditionalRequest = (config: SendConditionalRequestConfig) => { +export const useConditionalRequest = ( + config: SendConditionalRequestConfig +) => { const [state, setState] = useState<{ - error: Error | null; + error: RequestError | null; data: D | null; isLoading: boolean; }>({ @@ -70,7 +76,7 @@ export const useConditionalRequest = (config: SendConditionalRequestCon isLoading: true, error: null, }); - const res = await sendRequest({ + const res = await sendRequest({ method: config.method, path: config.path, query: config.query, diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/edit_package_config_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/edit_package_config_page/index.tsx index 7fbcdbb9738c..52fd95d66367 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/edit_package_config_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/edit_package_config_page/index.tsx @@ -16,7 +16,7 @@ import { EuiFlexItem, EuiSpacer, } from '@elastic/eui'; -import { AgentConfig, PackageInfo, NewPackageConfig } from '../../../types'; +import { AgentConfig, PackageInfo, UpdatePackageConfig } from '../../../types'; import { useLink, useBreadcrumbs, @@ -72,7 +72,7 @@ export const EditPackageConfigPage: React.FunctionComponent = () => { const [loadingError, setLoadingError] = useState(); const [agentConfig, setAgentConfig] = useState(); const [packageInfo, setPackageInfo] = useState(); - const [packageConfig, setPackageConfig] = useState({ + const [packageConfig, setPackageConfig] = useState({ name: '', description: '', namespace: '', @@ -80,6 +80,7 @@ export const EditPackageConfigPage: React.FunctionComponent = () => { enabled: true, output_id: '', inputs: [], + version: '', }); // Retrieve agent config, package, and package config info @@ -160,7 +161,7 @@ export const EditPackageConfigPage: React.FunctionComponent = () => { const hasErrors = validationResults ? validationHasErrors(validationResults) : false; // Update package config method - const updatePackageConfig = (updatedFields: Partial) => { + const updatePackageConfig = (updatedFields: Partial) => { const newPackageConfig = { ...packageConfig, ...updatedFields, @@ -178,7 +179,7 @@ export const EditPackageConfigPage: React.FunctionComponent = () => { } }; - const updatePackageConfigValidation = (newPackageConfig?: NewPackageConfig) => { + const updatePackageConfigValidation = (newPackageConfig?: UpdatePackageConfig) => { if (packageInfo) { const newValidationResult = validatePackageConfig( newPackageConfig || packageConfig, @@ -234,9 +235,31 @@ export const EditPackageConfigPage: React.FunctionComponent = () => { : undefined, }); } else { - notifications.toasts.addError(error, { - title: 'Error', - }); + if (error.statusCode === 409) { + notifications.toasts.addError(error, { + title: i18n.translate('xpack.ingestManager.editPackageConfig.failedNotificationTitle', { + defaultMessage: `Error updating '{packageConfigName}'`, + values: { + packageConfigName: packageConfig.name, + }, + }), + toastMessage: i18n.translate( + 'xpack.ingestManager.editPackageConfig.failedConflictNotificationMessage', + { + defaultMessage: `Data is out of date. Refresh the page to get the latest configuration.`, + } + ), + }); + } else { + notifications.toasts.addError(error, { + title: i18n.translate('xpack.ingestManager.editPackageConfig.failedNotificationTitle', { + defaultMessage: `Error updating '{packageConfigName}'`, + values: { + packageConfigName: packageConfig.name, + }, + }), + }); + } setFormState('VALID'); } }; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts index 43ec2f6d1a74..e28d76cae995 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts @@ -15,6 +15,7 @@ export { EnrollmentAPIKey, PackageConfig, NewPackageConfig, + UpdatePackageConfig, PackageConfigInput, PackageConfigInputStream, PackageConfigConfigRecordEntry, diff --git a/x-pack/plugins/ingest_manager/server/routes/package_config/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/package_config/handlers.ts index e212c861ce77..f11275c92bb6 100644 --- a/x-pack/plugins/ingest_manager/server/routes/package_config/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/package_config/handlers.ts @@ -178,7 +178,7 @@ export const updatePackageConfigHandler: RequestHandler< }); } catch (e) { return response.customError({ - statusCode: 500, + statusCode: e.statusCode || 500, body: { message: e.message }, }); } diff --git a/x-pack/plugins/ingest_manager/server/services/package_config.ts b/x-pack/plugins/ingest_manager/server/services/package_config.ts index 5a7546bfee2e..9fa51d025ad2 100644 --- a/x-pack/plugins/ingest_manager/server/services/package_config.ts +++ b/x-pack/plugins/ingest_manager/server/services/package_config.ts @@ -15,6 +15,7 @@ import { import { PACKAGE_CONFIG_SAVED_OBJECT_TYPE } from '../constants'; import { NewPackageConfig, + UpdatePackageConfig, PackageConfig, ListWithKuery, PackageConfigSOAttributes, @@ -60,6 +61,7 @@ class PackageConfigService { return { id: newSo.id, + version: newSo.version, ...newSo.attributes, }; } @@ -71,7 +73,7 @@ class PackageConfigService { options?: { user?: AuthenticatedUser } ): Promise { const isoDate = new Date().toISOString(); - const { saved_objects: newSos } = await soClient.bulkCreate>( + const { saved_objects: newSos } = await soClient.bulkCreate( packageConfigs.map((packageConfig) => ({ type: SAVED_OBJECT_TYPE, attributes: { @@ -98,6 +100,7 @@ class PackageConfigService { return newSos.map((newSo) => ({ id: newSo.id, + version: newSo.version, ...newSo.attributes, })); } @@ -117,6 +120,7 @@ class PackageConfigService { return { id: packageConfigSO.id, + version: packageConfigSO.version, ...packageConfigSO.attributes, }; } @@ -137,6 +141,7 @@ class PackageConfigService { return packageConfigSO.saved_objects.map((so) => ({ id: so.id, + version: so.version, ...so.attributes, })); } @@ -163,8 +168,9 @@ class PackageConfigService { }); return { - items: packageConfigs.saved_objects.map((packageConfigSO) => ({ + items: packageConfigs.saved_objects.map((packageConfigSO) => ({ id: packageConfigSO.id, + version: packageConfigSO.version, ...packageConfigSO.attributes, })), total: packageConfigs.total, @@ -176,21 +182,29 @@ class PackageConfigService { public async update( soClient: SavedObjectsClientContract, id: string, - packageConfig: NewPackageConfig, + packageConfig: UpdatePackageConfig, options?: { user?: AuthenticatedUser } ): Promise { const oldPackageConfig = await this.get(soClient, id); + const { version, ...restOfPackageConfig } = packageConfig; if (!oldPackageConfig) { throw new Error('Package config not found'); } - await soClient.update(SAVED_OBJECT_TYPE, id, { - ...packageConfig, - revision: oldPackageConfig.revision + 1, - updated_at: new Date().toISOString(), - updated_by: options?.user?.username ?? 'system', - }); + await soClient.update( + SAVED_OBJECT_TYPE, + id, + { + ...restOfPackageConfig, + revision: oldPackageConfig.revision + 1, + updated_at: new Date().toISOString(), + updated_by: options?.user?.username ?? 'system', + }, + { + version, + } + ); // Bump revision of associated agent config await agentConfigService.bumpRevision(soClient, packageConfig.config_id, { diff --git a/x-pack/plugins/ingest_manager/server/types/index.tsx b/x-pack/plugins/ingest_manager/server/types/index.tsx index 179474d31bc1..8239302a9783 100644 --- a/x-pack/plugins/ingest_manager/server/types/index.tsx +++ b/x-pack/plugins/ingest_manager/server/types/index.tsx @@ -21,6 +21,7 @@ export { PackageConfigInput, PackageConfigInputStream, NewPackageConfig, + UpdatePackageConfig, PackageConfigSOAttributes, FullAgentConfigInput, FullAgentConfig, diff --git a/x-pack/plugins/ingest_manager/server/types/models/package_config.ts b/x-pack/plugins/ingest_manager/server/types/models/package_config.ts index 4b9718dfbe16..0823ccd85a32 100644 --- a/x-pack/plugins/ingest_manager/server/types/models/package_config.ts +++ b/x-pack/plugins/ingest_manager/server/types/models/package_config.ts @@ -66,7 +66,13 @@ export const NewPackageConfigSchema = schema.object({ ...PackageConfigBaseSchema, }); +export const UpdatePackageConfigSchema = schema.object({ + ...PackageConfigBaseSchema, + version: schema.maybe(schema.string()), +}); + export const PackageConfigSchema = schema.object({ ...PackageConfigBaseSchema, id: schema.string(), + version: schema.maybe(schema.string()), }); diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/package_config.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/package_config.ts index 7b7ae1957c15..630fb55f2654 100644 --- a/x-pack/plugins/ingest_manager/server/types/rest_spec/package_config.ts +++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/package_config.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { schema } from '@kbn/config-schema'; -import { NewPackageConfigSchema } from '../models'; +import { NewPackageConfigSchema, UpdatePackageConfigSchema } from '../models'; import { ListWithKuerySchema } from './index'; export const GetPackageConfigsRequestSchema = { @@ -23,7 +23,7 @@ export const CreatePackageConfigRequestSchema = { export const UpdatePackageConfigRequestSchema = { ...GetOnePackageConfigRequestSchema, - body: NewPackageConfigSchema, + body: UpdatePackageConfigSchema, }; export const DeletePackageConfigsRequestSchema = {