diff --git a/app/javascript/components/automate-entry-points/index.jsx b/app/javascript/components/automate-entry-points/index.jsx index e5e5f805152..8638120a91e 100644 --- a/app/javascript/components/automate-entry-points/index.jsx +++ b/app/javascript/components/automate-entry-points/index.jsx @@ -177,6 +177,7 @@ const AutomateEntryPoints = ({ return !isLoading && ( { requestObject.config_info.provision.extra_vars = convertArrayToObject(requestObject.config_info.provision.extra_vars); } + if (requestObject.provisioning_entry_point_type === 'embedded_automate') { + if (requestObject.provisioning_entry_point_automate && requestObject.provisioning_entry_point_automate.element) { + requestObject.provisioning_entry_point = { + id: requestObject.provisioning_entry_point_automate.element.id, + name: requestObject.provisioning_entry_point_automate.element.name, + }; + delete requestObject.provisioning_entry_point_automate; + } + } else if (requestObject.provisioning_entry_point_workflow) { + requestObject.provisioning_entry_point = requestObject.provisioning_entry_point_workflow.id; + delete requestObject.provisioning_entry_point_workflow; + } + delete requestObject.provisioning_entry_point_type; + + if (requestObject.reconfigure_entry_point_type === 'embedded_automate') { + if (requestObject.reconfigure_entry_point_automate && requestObject.reconfigure_entry_point_automate.element) { + requestObject.reconfigure_entry_point = { + id: requestObject.reconfigure_entry_point_automate.element.id, + name: requestObject.reconfigure_entry_point_automate.element.name, + }; + delete requestObject.reconfigure_entry_point_automate; + } + } else if (requestObject.reconfigure_entry_point_workflow) { + requestObject.reconfigure_entry_point = requestObject.reconfigure_entry_point_workflow.id; + delete requestObject.reconfigure_entry_point_workflow; + } + delete requestObject.reconfigure_entry_point_type; + + if (requestObject.retirement_entry_point_type === 'embedded_automate') { + if (requestObject.retirement_entry_point_automate && requestObject.retirement_entry_point_automate.element) { + requestObject.retirement_entry_point = { + id: requestObject.reconfigure_entry_point_automate.element.id, + name: requestObject.retirement_entry_point_automate.element.name, + }; + delete requestObject.retirement_entry_point_automate; + } + } else if (requestObject.retirement_entry_point_workflow) { + requestObject.retirement_entry_point = requestObject.retirement_entry_point_workflow.id; + delete requestObject.retirement_entry_point_workflow; + } + delete requestObject.retirement_entry_point_type; + // if (requestObject.config_info.retirement.extra_vars) { // requestObject.config_info.retirement.extra_vars = convertArrayToObject(requestObject.config_info.retirement.extra_vars); // } diff --git a/app/javascript/components/terraform-template-catalog-form/terraform-template-catalog-form.schema.js b/app/javascript/components/terraform-template-catalog-form/terraform-template-catalog-form.schema.js index b4b93512ed4..0d1fb905ce6 100644 --- a/app/javascript/components/terraform-template-catalog-form/terraform-template-catalog-form.schema.js +++ b/app/javascript/components/terraform-template-catalog-form/terraform-template-catalog-form.schema.js @@ -30,6 +30,108 @@ const basicInformationTabSchema = (availableCatalogs, tenantTree, roleAllows, zo id: 'description', label: __('Description'), }, + { + component: componentTypes.SELECT, + id: 'provisioning_entry_point_type', + name: 'provisioning_entry_point_type', + label: __('Provisioning Entry Point'), + initialValue: 'embedded_automate', + options: [{ value: 'embedded_automate', label: __('Embedded Automate') }, { value: 'embedded_workflow', label: __('Embedded Workflow') }], + }, + { + component: 'embedded-automate-entry-point', + id: 'provisioning_entry_point_automate', + name: 'provisioning_entry_point_automate', + label: 'Provisioning Entry Point', + field: 'fqname', + selected: '', + type: 'provision', + condition: { + when: 'provisioning_entry_point_type', + is: 'embedded_automate', + }, + }, + { + component: 'embedded-workflow-entry-point', + id: 'provisioning_entry_point_workflow', + name: 'provisioning_entry_point_workflow', + label: 'Provisioning Entry Point', + field: 'fqname', + selected: '', + type: 'provision', + condition: { + when: 'provisioning_entry_point_type', + is: 'embedded_workflow', + }, + }, + { + component: componentTypes.SELECT, + id: 'reconfigure_entry_point_type', + name: 'reconfigure_entry_point_type', + label: __('Reconfigure Entry Point'), + initialValue: 'embedded_automate', + options: [{ value: 'embedded_automate', label: __('Embedded Automate') }, { value: 'embedded_workflow', label: __('Embedded Workflow') }], + }, + { + component: 'embedded-automate-entry-point', + id: 'reconfigure_entry_point_automate', + name: 'reconfigure_entry_point_automate', + label: 'Reconfigure Entry Point', + field: 'fqname', + selected: '', + type: 'provision', + condition: { + when: 'reconfigure_entry_point_type', + is: 'embedded_automate', + }, + }, + { + component: 'embedded-workflow-entry-point', + id: 'reconfigure_entry_point_workflow', + name: 'reconfigure_entry_point_workflow', + label: 'Reconfigure Entry Point', + field: 'fqname', + selected: '', + type: 'provision', + condition: { + when: 'reconfigure_entry_point_type', + is: 'embedded_workflow', + }, + }, + { + component: componentTypes.SELECT, + id: 'retirement_entry_point_type', + name: 'retirement_entry_point_type', + label: __('Retirement Entry Point'), + initialValue: 'embedded_automate', + options: [{ value: 'embedded_automate', label: __('Embedded Automate') }, { value: 'embedded_workflow', label: __('Embedded Workflow') }], + }, + { + component: 'embedded-automate-entry-point', + id: 'retirement_entry_point_automate', + name: 'retirement_entry_point_automate', + label: 'Retirement Entry Point', + field: 'fqname', + selected: '', + type: 'provision', + condition: { + when: 'retirement_entry_point_type', + is: 'embedded_automate', + }, + }, + { + component: 'embedded-workflow-entry-point', + id: 'retirement_entry_point_workflow', + name: 'retirement_entry_point_workflow', + label: 'Retirement Entry Point', + field: 'fqname', + selected: '', + type: 'provision', + condition: { + when: 'retirement_entry_point_type', + is: 'embedded_workflow', + }, + }, { component: componentTypes.CHECKBOX, name: 'display', @@ -198,6 +300,14 @@ const provisionTabSchema = ( label: __('Verbosity'), options: transformObjectToSelectOptions(verbosityTypes), }, + { + component: 'key-value-list', + id: 'config_info.provision.extra_vars', + name: 'config_info.provision.extra_vars', + label: __('Variables & Default Values'), + keyLabel: __('Variable'), + valueLabel: __('Default value'), + }, { component: componentTypes.RADIO, id: 'config_info.provision.dialog_type', @@ -237,6 +347,159 @@ const provisionTabSchema = ( return schema; }; +// const retirementTabSchema = ( +// repositories, +// setData, +// retirementRepositoryId, +// currentRegion, +// retirementEsclationDisplay, +// cloudTypes, +// retirementCloudType, +// logOutputTypes, +// verbosityTypes +// ) => { +// const schema = { +// component: componentTypes.TAB_ITEM, +// id: 'retirement-tab', +// name: 'retirement-tab', +// label: __('Retirement'), +// fields: [ +// { +// component: 'copy-from-provisioning', +// id: 'config_info.retirement.copyFromProvisioning', +// name: 'config_info.retirement.copyFromProvisioning', +// label: __('Copy from Provisioning'), +// copyFrom: ['repository_id', 'configuration_script_payload_id', 'credential_id', 'cloud_type'], +// copyTo: ['repository_id', 'configuration_script_payload_id', 'credential_id', 'cloud_type'], +// }, +// { +// component: componentTypes.SELECT, +// id: 'config_info.retirement.repository_id', +// name: 'config_info.retirement.repository_id', +// label: __('Repository'), +// options: transformGeneralOptions(repositories), +// includeEmpty: true, +// onChange: (repositoryId) => setData((state) => ({ ...state, retirementRepositoryId: repositoryId })), +// }, +// { +// component: componentTypes.SELECT, +// id: 'config_info.retirement.configuration_script_payload_id', +// name: 'config_info.retirement.configuration_script_payload_id', +// label: __('Template'), +// loadOptions: () => (retirementRepositoryId ? loadRepositoryOptions(retirementRepositoryId, currentRegion) : Promise.resolve([])), +// condition: { +// when: 'config_info.retirement.repository_id', +// isNotEmpty: true, +// }, +// key: `${retirementRepositoryId}-retirement-payload_id`, +// validateOnMount: true, +// validate: [{ type: 'customValidatorForRetirementFields' }], +// includeEmpty: true, +// }, +// { +// component: 'conditional-checkbox', +// id: 'config_info.retirement.become_method', +// name: 'config_info.retirement.become_method', +// label: __('Escalate Privilege'), +// display: retirementEsclationDisplay, +// }, +// { +// component: componentTypes.SELECT, +// id: 'config_info.retirement.cloud_type', +// name: 'config_info.retirement.cloud_type', +// label: __('Cloud Type'), +// options: transformcloudTypesOptions(cloudTypes), +// onChange: (cloudType) => setData((state) => ({ ...state, retirementCloudType: cloudType })), +// includeEmpty: true, +// condition: { +// when: 'config_info.retirement.repository_id', +// isNotEmpty: true, +// }, +// }, +// { +// component: componentTypes.SELECT, +// id: 'config_info.retirement.credential_id', +// name: 'config_info.retirement.credential_id', +// label: __('Credential'), +// options: transformcloudTypesOptions(cloudTypes), +// loadOptions: () => (retirementCloudType ? loadCloudCredentialOptions(retirementCloudType) : Promise.resolve([])), +// key: `${retirementCloudType}-retirement-cloud-credentail-id`, +// includeEmpty: true, +// condition: { +// and: [ +// { +// when: 'config_info.retirement.cloud_type', +// isNotEmpty: true, +// }, +// { +// when: 'config_info.retirement.repository_id', +// isNotEmpty: true, +// }, +// ], +// }, +// }, +// { +// component: componentTypes.TEXT_FIELD, +// id: 'config_info.retirement.execution_ttl', +// name: 'config_info.retirement.execution_ttl', +// label: __('Max TTL (mins)'), +// dataType: 'number', +// }, +// { +// component: componentTypes.SELECT, +// id: 'config_info.retirement.log_output', +// name: 'config_info.retirement.log_output', +// label: __('Logging Output'), +// options: transformObjectToSelectOptions(logOutputTypes), +// }, +// { +// component: componentTypes.SELECT, +// id: 'config_info.retirement.verbosity', +// name: 'config_info.retirement.verbosity', +// label: __('Verbosity'), +// options: transformObjectToSelectOptions(verbosityTypes), +// }, +// { +// component: componentTypes.SELECT, +// id: 'config_info.retirement.remove_resources', +// name: 'config_info.retirement.remove_resources', +// label: __('Remove resources?'), +// options: [ +// { label: 'No', value: 'no_with_playbook' }, +// { label: 'Before Template runs', value: 'pre_with_playbook' }, +// { label: 'After Template runs', value: "post_with_playbook'" }, +// ], +// condition: { +// when: 'config_info.retirement.repository_id', +// isNotEmpty: true, +// }, +// }, +// { +// component: componentTypes.SELECT, +// id: 'config_info.retirement.remove_resources_with_no_repistory_id', +// name: 'config_info.retirement.remove_resources_with_no_repistory_id', +// label: __('Remove resources?'), +// options: [ +// { label: 'No', value: 'no_without_playbook' }, +// { label: 'Yes', value: 'yes_without_playbook' }, +// ], +// condition: { +// when: 'config_info.retirement.repository_id', +// isEmpty: true, +// }, +// }, +// { +// component: 'key-value-list', +// id: 'config_info.retirement.extra_vars', +// name: 'config_info.retirement.extra_vars', +// label: __('Variables & Default Values'), +// keyLabel: __('Variable'), +// valueLabel: __('Default value'), +// }, +// ], +// }; +// return schema; +// }; const createSchema = ({ data, @@ -251,10 +514,13 @@ const createSchema = ({ currencies, repositories, provisionRepositoryId, + // retirementRepositoryId, cloudTypes, dialogs, provisionCloudType, + // retirementCloudType, provisionEsclationDisplay, + // retirementEsclationDisplay, } = data; const { @@ -292,6 +558,19 @@ const createSchema = ({ dialogs ), }, + // { + // ...retirementTabSchema( + // repositories, + // setData, + // retirementRepositoryId, + // currentRegion, + // retirementEsclationDisplay, + // cloudTypes, + // retirementCloudType, + // logOutputTypes, + // verbosityTypes + // ), + // }, ], }, ]; diff --git a/app/javascript/components/workflows/helper.js b/app/javascript/components/workflows/helper.js index 42a4434690c..928cd27dd81 100644 --- a/app/javascript/components/workflows/helper.js +++ b/app/javascript/components/workflows/helper.js @@ -2,11 +2,13 @@ import { rowData } from '../miq-data-table/helper'; /** Function to return the header information for the service catalog item's entry points. */ const entryPointsHeaderInfo = () => [ - { header: __('Name'), key: 'name' }, + { header: __('Repository'), key: 'configuration_script_source.name' }, + { header: __('Workflow name'), key: 'name' }, ]; /** Function to return the cell data for a row item. */ -const celInfo = (workflow) => [ +const cellInfo = (workflow) => [ + { text: workflow.configuration_script_source ? workflow.configuration_script_source.name : '' }, { text: workflow.name }, ]; @@ -14,7 +16,7 @@ const celInfo = (workflow) => [ const rowInfo = (headers, response) => { const headerKeys = headers.map((item) => item.key); const rows = response.resources.filter((item) => item.payload).map((workflow) => ({ - id: workflow.id.toString(), cells: celInfo(workflow), clickable: true, + id: workflow.id.toString(), cells: cellInfo(workflow), clickable: true, })); const miqRows = rowData(headerKeys, rows, false); return miqRows.rowItems; diff --git a/app/javascript/components/workflows/workflow-entry-points.jsx b/app/javascript/components/workflows/workflow-entry-points.jsx index 2012f59874f..f7d37463f8d 100644 --- a/app/javascript/components/workflows/workflow-entry-points.jsx +++ b/app/javascript/components/workflows/workflow-entry-points.jsx @@ -9,8 +9,11 @@ const WorkflowEntryPoints = ({ field, selected, type, setShowModal, setSelectedValue, }) => { const [data, setData] = useState({ - isLoading: true, list: {}, selectedItemId: selected, + isLoading: true, list: {}, selectedItemId: selected, key: 'workflow-entry-points', }); + const [prevSelectedHeader, setPrevSelectedHeader] = useState(''); + const [sortDirectionRepository, setSortDirectionRepository] = useState('DESC'); + const [sortDirectionName, setSortDirectionName] = useState('DESC'); const workflowTypes = { provision: __('Provision'), @@ -18,10 +21,68 @@ const WorkflowEntryPoints = ({ retire: __('Retirement'), }; + const sortFunction = (selectedHeader, itemA, itemB) => { + if (selectedHeader.key === 'name') { + if (itemA.name.text < itemB.name.text) { + return -1; + } if (itemA.name.text > itemB.name.text) { + return 1; + } if (itemA.name.text === itemB.name.text) { + return itemA.id - itemB.id; + } + } + if (itemA['configuration_script_source.name'] === undefined) { + itemA['configuration_script_source.name'] = { text: '' }; + } else if (itemB['configuration_script_source.name'] === undefined) { + itemB['configuration_script_source.name'] = { text: '' }; + } + if (itemA['configuration_script_source.name'].text < itemB['configuration_script_source.name'].text) { + return -1; + } if (itemA['configuration_script_source.name'].text > itemB['configuration_script_source.name'].text) { + return 1; + } + return 0; + }; + + const onSort = (itemKey) => { + const selectedHeader = data.list.headers.find((item) => item === itemKey); + if (selectedHeader) { + const sortedList = data.list; + sortedList.rows.sort((a, b) => sortFunction(selectedHeader, a, b)); + if (prevSelectedHeader === selectedHeader.key) { + if (selectedHeader.key === 'name') { + if (sortDirectionName === 'ASC') { + sortedList.rows.sort((a, b) => sortFunction(selectedHeader, a, b)); + } else { + sortedList.rows.sort((a, b) => sortFunction(selectedHeader, b, a)); + } + setSortDirectionName(sortDirectionName === 'ASC' ? 'DESC' : 'ASC'); + } else { + if (sortDirectionRepository === 'ASC') { + sortedList.rows.sort((a, b) => sortFunction(selectedHeader, a, b)); + } else { + sortedList.rows.sort((a, b) => sortFunction(selectedHeader, b, a)); + } + setSortDirectionRepository(sortDirectionRepository === 'ASC' ? 'DESC' : 'ASC'); + } + } else { + setSortDirectionName('DESC'); + setSortDirectionRepository('DESC'); + } + const tempKey = `${data.key}-${sortedList.rows[0].id}`; + setPrevSelectedHeader(selectedHeader.key); + setData({ + ...data, isLoading: false, list: sortedList, key: tempKey, + }); + } + }; + useEffect(() => { http.post(`/catalog/ae_tree_select_toggle?typ=${type}`, {}, { headers: {}, skipJsonParsing: true }) .then((_data) => { - API.get('/api/configuration_script_payloads?expand=resources') + const url = '/api/configuration_script_payloads/?expand=resources&attributes=configuration_script_source.name&' + + 'collection_class=ManageIQ::Providers::Workflows::AutomationManager::Workflow'; + API.get(url) .then((response) => { setData({ ...data, @@ -81,6 +142,7 @@ const WorkflowEntryPoints = ({ open modalHeading={sprintf(__('Select Embedded Workflow - %s Entry Point'), workflowTypes[type])} primaryButtonText={__('Apply')} + primaryButtonDisabled={!data.selectedItemId} secondaryButtonText={__('Cancel')} onRequestSubmit={onApply} onRequestClose={onCloseModal} @@ -90,6 +152,9 @@ const WorkflowEntryPoints = ({ onSelect(selectedRow.id)} showPagination={false} truncateText={false}