diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml
index 599289b7c6eacc..2960327b9087e9 100644
--- a/.buildkite/ftr_configs.yml
+++ b/.buildkite/ftr_configs.yml
@@ -225,7 +225,7 @@ enabled:
- x-pack/test/functional_basic/apps/ml/data_visualizer/group2/config.ts
- x-pack/test/functional_basic/apps/ml/data_visualizer/group3/config.ts
- x-pack/test/functional_basic/apps/transform/creation/index_pattern/config.ts
- - x-pack/test/functional_basic/apps/transform/start_reset_delete/config.ts
+ - x-pack/test/functional_basic/apps/transform/actions/config.ts
- x-pack/test/functional_basic/apps/transform/edit_clone/config.ts
- x-pack/test/functional_basic/apps/transform/creation/runtime_mappings_saved_search/config.ts
- x-pack/test/functional_basic/apps/transform/permissions/config.ts
@@ -291,7 +291,7 @@ enabled:
- x-pack/test/functional/apps/status_page/config.ts
- x-pack/test/functional/apps/transform/creation/index_pattern/config.ts
- x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/config.ts
- - x-pack/test/functional/apps/transform/start_reset_delete/config.ts
+ - x-pack/test/functional/apps/transform/actions/config.ts
- x-pack/test/functional/apps/transform/edit_clone/config.ts
- x-pack/test/functional/apps/transform/permissions/config.ts
- x-pack/test/functional/apps/transform/feature_controls/config.ts
diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx
index fd79e8eb9e2e9e..3c74cf450b4b94 100644
--- a/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx
+++ b/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx
@@ -114,6 +114,7 @@ export const TransformManagement: FC = () => {
diff --git a/x-pack/plugins/transform/readme.md b/x-pack/plugins/transform/readme.md
index 7eb1f59250be9d..8c25f2ddd8ac46 100644
--- a/x-pack/plugins/transform/readme.md
+++ b/x-pack/plugins/transform/readme.md
@@ -122,7 +122,7 @@ With PATH_TO_CONFIG and other options as follows.
edit, clone | `test/functional/apps/transform/edit_clone/config.ts`
feature controls | `test/functional/apps/transform/feature_controls/config.ts`
permissions | `test/functional/apps/transform/permissions/config.ts`
- start, reset, delete | `test/functional/apps/transform/start_reset_delete/config.ts`
+ actions | `test/functional/apps/transform/actions/config.ts`
1. Functional UI tests with `Basic` license:
@@ -133,7 +133,7 @@ With PATH_TO_CONFIG and other options as follows.
edit, clone | `test/functional_basic/apps/transform/edit_clone/config.ts`
feature controls | `test/functional_basic/apps/transform/feature_controls/config.ts`
permissions | `test/functional_basic/apps/transform/permissions/config.ts`
- start, reset, delete | `test/functional_basic/apps/transform/start_reset_delete/config.ts`
+ actions | `test/functional_basic/apps/transform/actions/config.ts`
1. API integration tests with `Trial` license:
diff --git a/x-pack/test/api_integration/apis/transform/reauthorize_transforms.ts b/x-pack/test/api_integration/apis/transform/reauthorize_transforms.ts
index 274c1b42a4d07a..a33ef09063d37a 100644
--- a/x-pack/test/api_integration/apis/transform/reauthorize_transforms.ts
+++ b/x-pack/test/api_integration/apis/transform/reauthorize_transforms.ts
@@ -21,9 +21,6 @@ export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertestWithoutAuth');
const transform = getService('transform');
- // If transform was created with sufficient -> should still authorize and start
- // If transform was created with insufficient -> should still authorize and start
-
function getTransformIdByUser(username: USER) {
return `transform-by-${username}`;
}
@@ -51,6 +48,8 @@ export default ({ getService }: FtrProviderContext) => {
await transform.api.deleteIndices(destinationIndex);
}
+ // If transform was created with sufficient permissions -> should create and start
+ // If transform was created with insufficient permissions -> should create but not start
describe('/api/transform/reauthorize_transforms', function () {
const apiKeysForTransformUsers = new Map();
diff --git a/x-pack/test/functional/apps/transform/start_reset_delete/config.ts b/x-pack/test/functional/apps/transform/actions/config.ts
similarity index 96%
rename from x-pack/test/functional/apps/transform/start_reset_delete/config.ts
rename to x-pack/test/functional/apps/transform/actions/config.ts
index edf34d16785c4c..3b7948e9c66bc5 100644
--- a/x-pack/test/functional/apps/transform/start_reset_delete/config.ts
+++ b/x-pack/test/functional/apps/transform/actions/config.ts
@@ -14,7 +14,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
...functionalConfig.getAll(),
testFiles: [require.resolve('.')],
junit: {
- reportName: 'Chrome X-Pack UI Functional Tests - transform - start reset & delete',
+ reportName: 'Chrome X-Pack UI Functional Tests - transform - actions',
},
};
}
diff --git a/x-pack/test/functional/apps/transform/start_reset_delete/deleting.ts b/x-pack/test/functional/apps/transform/actions/deleting.ts
similarity index 100%
rename from x-pack/test/functional/apps/transform/start_reset_delete/deleting.ts
rename to x-pack/test/functional/apps/transform/actions/deleting.ts
diff --git a/x-pack/test/functional/apps/transform/start_reset_delete/index.ts b/x-pack/test/functional/apps/transform/actions/index.ts
similarity index 92%
rename from x-pack/test/functional/apps/transform/start_reset_delete/index.ts
rename to x-pack/test/functional/apps/transform/actions/index.ts
index 1a606339eb82a9..8a156b4bba84d4 100644
--- a/x-pack/test/functional/apps/transform/start_reset_delete/index.ts
+++ b/x-pack/test/functional/apps/transform/actions/index.ts
@@ -11,7 +11,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const transform = getService('transform');
- describe('transform - start reset & delete', function () {
+ describe('transform - actions', function () {
this.tags('transform');
before(async () => {
@@ -33,6 +33,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
});
loadTestFile(require.resolve('./deleting'));
+ loadTestFile(require.resolve('./reauthorizing'));
loadTestFile(require.resolve('./resetting'));
loadTestFile(require.resolve('./starting'));
});
diff --git a/x-pack/test/functional/apps/transform/actions/reauthorizing.ts b/x-pack/test/functional/apps/transform/actions/reauthorizing.ts
new file mode 100644
index 00000000000000..89acef449c2e7c
--- /dev/null
+++ b/x-pack/test/functional/apps/transform/actions/reauthorizing.ts
@@ -0,0 +1,221 @@
+/*
+ * 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 { TRANSFORM_HEALTH_LABEL, TRANSFORM_STATE } from '@kbn/transform-plugin/common/constants';
+import type {
+ TransformLatestConfig,
+ TransformPivotConfig,
+} from '@kbn/transform-plugin/common/types/transform';
+import type { SecurityCreateApiKeyResponse } from '@elastic/elasticsearch/lib/api/types';
+import type { FtrProviderContext } from '../../../ftr_provider_context';
+import { getLatestTransformConfig, getPivotTransformConfig } from '../helpers';
+import { USER } from '../../../services/transform/security_common';
+import { COMMON_REQUEST_HEADERS } from '../../../services/ml/common_api';
+
+interface TestDataPivot {
+ suiteTitle: string;
+ originalConfig: TransformPivotConfig;
+ mode: 'batch' | 'continuous';
+ type: 'pivot';
+ expected: {
+ originalState: object;
+ reauthorizeEnabled: boolean;
+ reauthorizedState: object;
+ };
+ created_by_user: USER;
+ current_user: USER;
+}
+
+interface TestDataLatest {
+ suiteTitle: string;
+ originalConfig: TransformLatestConfig;
+ mode: 'batch' | 'continuous';
+ type: 'latest';
+ expected: {
+ originalState: object;
+ reauthorizeEnabled: boolean;
+ reauthorizedState: object;
+ };
+ created_by_user: USER;
+ current_user: USER;
+}
+
+type TestData = TestDataPivot | TestDataLatest;
+
+function generateHeaders(apiKey: SecurityCreateApiKeyResponse) {
+ return {
+ ...COMMON_REQUEST_HEADERS,
+ 'es-secondary-authorization': `ApiKey ${apiKey.encoded}`,
+ };
+}
+
+export default function ({ getService }: FtrProviderContext) {
+ const esArchiver = getService('esArchiver');
+ const transform = getService('transform');
+
+ const apiKeysForTransformUsers = new Map();
+
+ describe('reauthorizing', function () {
+ const PREFIX = 'reauthorizing';
+ const testDataList: TestData[] = [
+ {
+ suiteTitle: 'continuous pivot transform (created by viewer, viewed by viewer)',
+ originalConfig: getPivotTransformConfig(`${PREFIX}-${USER.TRANSFORM_VIEWER}-1`, true),
+ mode: 'continuous',
+ type: 'pivot',
+ expected: {
+ originalState: { status: TRANSFORM_STATE.STOPPED, health: TRANSFORM_HEALTH_LABEL.red },
+ reauthorizeEnabled: false,
+ reauthorizedState: {
+ status: TRANSFORM_STATE.STARTED,
+ health: TRANSFORM_HEALTH_LABEL.green,
+ },
+ },
+ created_by_user: USER.TRANSFORM_VIEWER,
+ current_user: USER.TRANSFORM_VIEWER,
+ },
+ {
+ suiteTitle: 'batch pivot transform (created by viewer, viewed by poweruser)',
+ originalConfig: getPivotTransformConfig(PREFIX, false),
+ mode: 'batch',
+ type: 'pivot',
+ expected: {
+ originalState: { status: TRANSFORM_STATE.STOPPED, health: TRANSFORM_HEALTH_LABEL.red },
+ reauthorizeEnabled: true,
+ reauthorizedState: {
+ status: TRANSFORM_STATE.STOPPED,
+ progress: '100',
+ health: TRANSFORM_HEALTH_LABEL.green,
+ },
+ },
+ created_by_user: USER.TRANSFORM_VIEWER,
+ current_user: USER.TRANSFORM_POWERUSER,
+ },
+ {
+ suiteTitle: 'continuous pivot transform (created by viewer, authorized by poweruser)',
+ originalConfig: getPivotTransformConfig(`${PREFIX}-${USER.TRANSFORM_VIEWER}-2`, true),
+ mode: 'continuous',
+ type: 'pivot',
+ expected: {
+ originalState: { status: TRANSFORM_STATE.STOPPED, health: TRANSFORM_HEALTH_LABEL.red },
+ reauthorizeEnabled: true,
+ reauthorizedState: {
+ status: TRANSFORM_STATE.STARTED,
+ health: TRANSFORM_HEALTH_LABEL.green,
+ },
+ },
+ created_by_user: USER.TRANSFORM_VIEWER,
+ current_user: USER.TRANSFORM_POWERUSER,
+ },
+ {
+ suiteTitle: 'continuous latest transform (created by poweruser, viewed by viewer)',
+ originalConfig: getLatestTransformConfig(`${PREFIX}-${USER.TRANSFORM_POWERUSER}-1`, true),
+ mode: 'continuous',
+ type: 'latest',
+ expected: {
+ originalState: { status: TRANSFORM_STATE.STARTED, health: TRANSFORM_HEALTH_LABEL.green },
+ reauthorizeEnabled: false,
+ reauthorizedState: {
+ status: TRANSFORM_STATE.STARTED,
+ health: TRANSFORM_HEALTH_LABEL.green,
+ },
+ },
+ created_by_user: USER.TRANSFORM_POWERUSER,
+ current_user: USER.TRANSFORM_VIEWER,
+ },
+ ];
+
+ before(async () => {
+ const apiKeyForTransformUsers =
+ await transform.securityCommon.createApiKeyForTransformUsers();
+
+ apiKeyForTransformUsers.forEach(({ user, apiKey }) =>
+ apiKeysForTransformUsers.set(user.name as USER, apiKey)
+ );
+
+ await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce');
+ await transform.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date');
+
+ for (const testData of testDataList) {
+ await transform.api.createTransform(testData.originalConfig.id, testData.originalConfig, {
+ deferValidation: true,
+ // Create transforms with secondary authorization headers
+ headers: generateHeaders(apiKeysForTransformUsers.get(testData.created_by_user)!),
+ });
+ // For transforms created with insufficient permissions, they can be created but not started
+ // so we should not assert that the api call is successful here
+ await transform.api.startTransform(testData.originalConfig.id, false);
+ }
+ await transform.testResources.setKibanaTimeZoneToUTC();
+ });
+
+ after(async () => {
+ await transform.securityCommon.clearAllTransformApiKeys();
+
+ for (const testData of testDataList) {
+ await transform.testResources.deleteIndexPatternByTitle(testData.originalConfig.dest.index);
+ await transform.api.deleteIndices(testData.originalConfig.dest.index);
+ }
+
+ await transform.api.cleanTransformIndices();
+ await transform.testResources.deleteIndexPatternByTitle('ft_ecommerce');
+ });
+
+ for (const testData of testDataList) {
+ const transformId = testData.originalConfig.id;
+
+ describe(`${testData.suiteTitle}`, function () {
+ it('reauthorize transform', async () => {
+ await transform.securityUI.loginAs(testData.current_user);
+
+ await transform.testExecution.logTestStep('should load the home page');
+ await transform.navigation.navigateTo();
+ await transform.management.assertTransformListPageExists();
+
+ await transform.testExecution.logTestStep('should display the transforms table');
+ await transform.management.assertTransformsTableExists();
+
+ await transform.testExecution.logTestStep(
+ 'should display the transforms reauthorize callout'
+ );
+ await transform.management.assertTransformsReauthorizeCalloutExists();
+
+ await transform.testExecution.logTestStep(
+ 'should display the original transform in the transform list'
+ );
+ await transform.table.filterWithSearchString(transformId, 1);
+ await transform.table.assertTransformRowFields(
+ transformId,
+ testData.expected.originalState
+ );
+
+ if (testData.expected.reauthorizeEnabled) {
+ await transform.testExecution.logTestStep('should reauthorize the transform');
+ await transform.table.assertTransformRowActionEnabled(
+ transformId,
+ 'Reauthorize',
+ testData.expected.reauthorizeEnabled
+ );
+ await transform.table.clickTransformRowAction(transformId, 'Reauthorize');
+ await transform.table.confirmReauthorizeTransform();
+
+ await transform.table.assertTransformRowFields(
+ transformId,
+ testData.expected.reauthorizedState
+ );
+ await transform.testExecution.logTestStep('should not show Reauthorize action anymore');
+ await transform.table.assertTransformRowActionMissing(transformId, 'Reauthorize');
+ } else {
+ await transform.testExecution.logTestStep('should show disabled action menu button');
+ await transform.table.assertTransformRowActionsButtonEnabled(transformId, false);
+ }
+ await transform.table.clearSearchString(testDataList.length);
+ });
+ });
+ }
+ });
+}
diff --git a/x-pack/test/functional/apps/transform/start_reset_delete/resetting.ts b/x-pack/test/functional/apps/transform/actions/resetting.ts
similarity index 100%
rename from x-pack/test/functional/apps/transform/start_reset_delete/resetting.ts
rename to x-pack/test/functional/apps/transform/actions/resetting.ts
diff --git a/x-pack/test/functional/apps/transform/start_reset_delete/starting.ts b/x-pack/test/functional/apps/transform/actions/starting.ts
similarity index 100%
rename from x-pack/test/functional/apps/transform/start_reset_delete/starting.ts
rename to x-pack/test/functional/apps/transform/actions/starting.ts
diff --git a/x-pack/test/functional/services/transform/api.ts b/x-pack/test/functional/services/transform/api.ts
index 4c6944b64a91d6..8fb984e72e0c5b 100644
--- a/x-pack/test/functional/services/transform/api.ts
+++ b/x-pack/test/functional/services/transform/api.ts
@@ -272,10 +272,13 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) {
});
},
- async startTransform(transformId: string) {
+ async startTransform(transformId: string, assertSuccess = true) {
log.debug(`Starting transform '${transformId}' ...`);
const { body, status } = await esSupertest.post(`/_transform/${transformId}/_start`);
- this.assertResponseStatusCode(200, status, body);
+
+ if (assertSuccess) {
+ this.assertResponseStatusCode(200, status, body);
+ }
},
async stopTransform(transformId: string) {
diff --git a/x-pack/test/functional/services/transform/management.ts b/x-pack/test/functional/services/transform/management.ts
index 1855159f478b61..4d5a54f38c7f86 100644
--- a/x-pack/test/functional/services/transform/management.ts
+++ b/x-pack/test/functional/services/transform/management.ts
@@ -32,6 +32,10 @@ export function TransformManagementProvider({ getService }: FtrProviderContext)
await testSubjects.existOrFail('transformCreateFirstButton');
},
+ async assertTransformsReauthorizeCalloutExists() {
+ await testSubjects.existOrFail('transformPageReauthorizeCallout');
+ },
+
async assertCreateFirstTransformButtonEnabled(expectedValue: boolean) {
const isEnabled = await testSubjects.isEnabled('transformCreateFirstButton');
expect(isEnabled).to.eql(
diff --git a/x-pack/test/functional/services/transform/transform_table.ts b/x-pack/test/functional/services/transform/transform_table.ts
index 135aa18af9b409..2af0d129ed51c9 100644
--- a/x-pack/test/functional/services/transform/transform_table.ts
+++ b/x-pack/test/functional/services/transform/transform_table.ts
@@ -9,7 +9,15 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
-type TransformRowActionName = 'Clone' | 'Delete' | 'Discover' | 'Edit' | 'Reset' | 'Start' | 'Stop';
+type TransformRowActionName =
+ | 'Clone'
+ | 'Delete'
+ | 'Discover'
+ | 'Edit'
+ | 'Reset'
+ | 'Start'
+ | 'Stop'
+ | 'Reauthorize';
export function TransformTableProvider({ getService }: FtrProviderContext) {
const find = getService('find');
@@ -464,6 +472,21 @@ export function TransformTableProvider({ getService }: FtrProviderContext) {
});
}
+ public async assertTransformRowActionMissing(
+ transformId: string,
+ action: TransformRowActionName
+ ) {
+ const selector = `transformAction${action}`;
+ await retry.tryForTime(60 * 1000, async () => {
+ await this.refreshTransformList();
+
+ await this.ensureTransformActionsMenuOpen(transformId);
+
+ await testSubjects.missingOrFail(selector, { timeout: 1000 });
+ await this.ensureTransformActionsMenuClosed();
+ });
+ }
+
public async assertTransformRowActionEnabled(
transformId: string,
action: TransformRowActionName,
@@ -516,6 +539,14 @@ export function TransformTableProvider({ getService }: FtrProviderContext) {
await testSubjects.missingOrFail('transformDeleteModal', { timeout: 60 * 1000 });
}
+ public async assertTransformReauthorizeModalExists() {
+ await testSubjects.existOrFail('transformReauthorizeModal', { timeout: 60 * 1000 });
+ }
+
+ public async assertTransformReauthorizeModalNotExists() {
+ await testSubjects.missingOrFail('transformReauthorizeModal', { timeout: 60 * 1000 });
+ }
+
public async assertTransformResetModalExists() {
await testSubjects.existOrFail('transformResetModal', { timeout: 60 * 1000 });
}
@@ -564,6 +595,14 @@ export function TransformTableProvider({ getService }: FtrProviderContext) {
});
}
+ public async confirmReauthorizeTransform() {
+ await retry.tryForTime(30 * 1000, async () => {
+ await this.assertTransformReauthorizeModalExists();
+ await testSubjects.click('transformReauthorizeModal > confirmModalConfirmButton');
+ await this.assertTransformReauthorizeModalNotExists();
+ });
+ }
+
public async confirmStartTransform() {
await retry.tryForTime(30 * 1000, async () => {
await this.assertTransformStartModalExists();
diff --git a/x-pack/test/functional_basic/apps/transform/start_reset_delete/config.ts b/x-pack/test/functional_basic/apps/transform/actions/config.ts
similarity index 85%
rename from x-pack/test/functional_basic/apps/transform/start_reset_delete/config.ts
rename to x-pack/test/functional_basic/apps/transform/actions/config.ts
index 6922e0f70c5a59..34c1359750082f 100644
--- a/x-pack/test/functional_basic/apps/transform/start_reset_delete/config.ts
+++ b/x-pack/test/functional_basic/apps/transform/actions/config.ts
@@ -16,8 +16,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
testFiles: [require.resolve('.')],
junit: {
...transformConfig.get('junit'),
- reportName:
- 'Chrome X-Pack UI Functional Tests Basic License - transform - start reset & delete',
+ reportName: 'Chrome X-Pack UI Functional Tests Basic License - transform - actions',
},
};
}
diff --git a/x-pack/test/functional_basic/apps/transform/start_reset_delete/index.ts b/x-pack/test/functional_basic/apps/transform/actions/index.ts
similarity index 95%
rename from x-pack/test/functional_basic/apps/transform/start_reset_delete/index.ts
rename to x-pack/test/functional_basic/apps/transform/actions/index.ts
index 14a9bcbc099c8b..589e6536ecc1b5 100644
--- a/x-pack/test/functional_basic/apps/transform/start_reset_delete/index.ts
+++ b/x-pack/test/functional_basic/apps/transform/actions/index.ts
@@ -12,6 +12,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
this.tags(['skipFirefox', 'transform']);
// The transform UI should work the same as with a trial license
- loadTestFile(require.resolve('../../../../functional/apps/transform/start_reset_delete'));
+ loadTestFile(require.resolve('../../../../functional/apps/transform/actions'));
});
}