/* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ import React, {useEffect, useState} from 'react'; import User from "../actions/user"; import {useLocation, useNavigate, useParams} from "react-router-dom"; import { capitalize, exportTable, getChanges, getNestedValuePath, userAutomationActionsMenuItems } from '../resources/main.js' import { Auth } from "@aws-amplify/auth"; import { SpaceBetween, Link, StatusIndicator } from '@awsui/components-react'; import ItemAmend from "../components/ItemAmend"; import WaveView from '../components/WaveView.jsx' import AutomationTools from '../components/AutomationTools.jsx' import ItemTable from '../components/ItemTable.jsx'; import { useModal } from '../actions/Modal.js'; import ToolsAPI from "../actions/tools"; //Hook imports import {useAutomationJobs} from "../actions/AutomationJobsHook"; import { useMFApps } from "../actions/ApplicationsHook"; import { useGetServers } from "../actions/ServersHook.js"; import { useMFWaves } from "../actions/WavesHook.js"; import AllViewerAttributes from "../components/ui_attributes/AllViewerAttributes"; import Audit from "../components/ui_attributes/Audit"; import {parsePUTResponseErrors} from "../resources/recordFunctions"; const Viewer = (props) => { const [viewerCurrentTab, setViewerCurrentTab] = useState('details'); function getCurrentWaveApplications() { if (props.selectedItems.length === 1) { let apps = props.dataAll.application.data.filter(function (entry) { return entry.wave_id === props.selectedItems[0].wave_id; }); if ( apps.length > 0){ return apps; } else { return []; } } } function getCurrentWaveServers() { if (props.selectedItems.length === 1) { let apps = getCurrentWaveApplications(); let servers = []; for(let item in apps) { let lservers = props.dataAll.server.data.filter(function (entry) { return entry.app_id === apps[item].app_id; }); servers = servers.concat(lservers); } if ( servers.length > 0){ return servers; } else { return []; } } } function getCurrentWaveJobs() { if (props.selectedItems.length === 1) { let jobs = props.dataAll.job.data.filter(function (entry) { //Return only jobs that have an argument with a Waveid matching the currently selected wave_id. if (entry.script.script_arguments) { return entry.script.script_arguments.Waveid === props.selectedItems[0].wave_id; } else { return false; } }); if ( jobs.length > 0){ return jobs; } else { return []; } } } async function handleViewerTabChange(tabselected) { setViewerCurrentTab(tabselected); } if (props.selectedItems.length === 1) { return ( //
); } else { return (null); } } const UserWaveTable = (props) => { let location = useLocation() let navigate = useNavigate(); let params = useParams(); //Data items for viewer and table. const [{ isLoading: isLoadingWaves, data: dataWaves, error: errorWaves }, { update: updateWaves }] = useMFWaves(); const [{ isLoading: isLoadingApps, data: dataApps, error: errorApps }, ] = useMFApps(); const [{ isLoading: isLoadingServers, data: dataServers, error: errorServers }, ] = useGetServers(); const [{ isLoading: isLoadingJobs, data: dataJobs, error: errorJobs }, ] = useAutomationJobs(); const dataAll = { job: {data: dataJobs, isLoading: isLoadingJobs, error: errorJobs}, application: {data: dataApps, isLoading: isLoadingApps, error: errorApps}, server: {data: dataServers, isLoading: isLoadingServers, error: errorServers}, wave: {data: dataWaves, isLoading: isLoadingWaves, error: errorWaves}}; //Main table state management. const [selectedItems, setSelectedItems] = useState([]); const [focusItem, setFocusItem] = useState([]); //Viewer pane state management. const [action, setAction] = useState('View'); const [actions, setActions] = useState([]); //Actions menu dropdown options. const [automationAction, setAutomationAction] = useState(undefined); const [focusSubItem, setFocusSubItem] = useState(undefined); const [preformingAction, setPreformingAction] = useState(false); //Get base path from the URL, all actions will use this base path. const basePath = location.pathname.split('/').length >= 2 ? '/' + location.pathname.split('/')[1] : '/'; //Key for main item displayed in table. const itemIDKey = 'wave_id'; const schemaName = 'wave'; //Modals const { show: showDeleteConfirmaton, hide: hideDeleteConfirmaton, RenderModal: DeleteModal } = useModal(); const { show: showLinkedRecord, hide: hideLinkedRecord, RenderModal: LinkedRecordModal } = useModal(); function handleNotification(notification) { return props.updateNotification('add', notification) } async function handleRefreshClick(e) { e.preventDefault(); await updateWaves(); } function handleAddItem() { navigate({ pathname: basePath + '/add' }) setAction('Add') setFocusItem({}); } function handleDownloadItems() { if (selectedItems.length > 0 ) { // Download selected only. exportTable(selectedItems, "Waves", "waves") } else { //Download all. exportTable(dataWaves, "Waves", "waves") } } function handleEditItem(selection = null) { if ( selectedItems.length === 1) { navigate({ pathname: basePath + '/edit/' + selectedItems[0][itemIDKey] }) setAction('Edit') setFocusItem(selectedItems[0]); } else if ( selection ) { navigate({ pathname: basePath + '/edit/' + selection[itemIDKey] }) setFocusItem(selection); setAction('Edit'); } } function handleResetScreen() { navigate({ pathname: basePath }) setAction('View'); } function handleItemSelectionChange(selection) { setSelectedItems(selection); if (selection.length === 1) { //TO-DO Need to pull in Waves or other data here. //updateApps(selection[0].app_id); } //Reset URL to base table path. navigate({ pathname: basePath }) } async function handleAction(actionData, actionId) { setPreformingAction(true); let newItem = Object.assign({}, actionData); let notificationId = null; let apiAction = props.schema[automationAction].actions.filter(function (entry) { return entry.id === actionId; }); if (apiAction.length !== 1) { handleNotification({ type: 'error', dismissible: true, header: "Perform wave action", content: props.schema[automationAction].friendly_name + ' action [' & actionId & '] not found in schema.', }); } else { try { if (apiAction[0].additionalData){ const keys = Object.keys(apiAction[0].additionalData); for (const i in keys){ newItem[keys[i]] = apiAction[0].additionalData[keys[i]] } } notificationId = handleNotification({ type: 'success', loading: true, dismissible: false, header: "Perform wave action", content: "Performing action - " + apiAction[0].name, }); const session = await Auth.currentSession(); const apiTools = await new ToolsAPI(session); const response = await apiTools.postTool(apiAction[0].apiPath, newItem); console.log(response) //Extra UUID from response. let uuid = response.split('+'); if (uuid.length > 1){ uuid = uuid[1]; await handleResetScreen(); handleNotification({ id: notificationId, type: 'success', dismissible: true, header: "Perform wave action", actionButtonTitle: "View Job", actionButtonLink: "/automation/jobs/" + uuid, content: apiAction[0].name + " action successfully.", }); } else { await handleResetScreen(); handleNotification({ id: notificationId, type: 'success', dismissible: true, header: "Perform wave action", content: response, }); } } catch (e) { console.log(e); if ('response' in e) { if(e.response != null && typeof e.response === 'object') { if ('data' in e.response) { handleNotification({ id: notificationId, type: 'error', dismissible: true, header: "Perform wave action1", content: apiAction[0].name + ' action failed: ' + (e.response.data.cause ? e.response.data.cause : e.response.data) }); } } else { handleNotification({ id: notificationId, type: 'error', dismissible: true, header: "Perform wave action2", content: apiAction[0].name + ' action failed: ' + e.message }); } } else { handleNotification({ id: notificationId, type: 'error', dismissible: true, header: "Perfrom wave action", content: apiAction[0].name + ' action failed: Unknown error occured', }); } } } setPreformingAction(false); } async function handleSave(editItem, action) { let newItem = Object.assign({}, editItem); try { if (action === 'Edit') { let wave_id = newItem.wave_id; let wave_ref = newItem.wave_name; newItem = getChanges(newItem, dataWaves, "wave_id"); if(!newItem){ // no changes to original record. handleNotification({ type: 'warning', dismissible: true, header: "Save " + schemaName, content: "No updates to save." }); return false; } delete newItem.wave_id; const session = await Auth.currentSession(); const apiUser = new User(session); let resultEdit = await apiUser.putItem(wave_id, newItem, 'wave'); if (resultEdit['errors']) { console.debug("PUT " + schemaName + " errors"); console.debug(resultEdit['errors']); let errorsReturned = parsePUTResponseErrors(resultEdit['errors']); handleNotification({ type: 'error', dismissible: true, header: "Update " + schemaName, content: (errorsReturned) }); } else { handleNotification({ type: 'success', dismissible: true, header: "Update " + schemaName, content: wave_ref + " updated successfully.", }); updateWaves(); handleResetScreen(); //This is needed to ensure the item in selectItems reflects new updates setSelectedItems([]); setFocusItem({}); } } else { const session = await Auth.currentSession(); const apiUser = new User(session); delete newItem.wave_id; let resultAdd = await apiUser.postItem(newItem, 'wave'); if (resultAdd['errors']) { console.debug("PUT " + schemaName + " errors"); console.debug(resultAdd['errors']); let errorsReturned = parsePUTResponseErrors(resultAdd['errors']); handleNotification({ type: 'error', dismissible: true, header: "Add " + schemaName, content: (errorsReturned) }); } else { handleNotification({ type: 'success', dismissible: true, header: "Add " + schemaName, content: newItem.wave_name + " added successfully.", }); updateWaves(); handleResetScreen(); } } } catch (e) { console.error(e); let response = ''; if ('response' in e && 'data' in e.response) { //Check if errors key exists from Lambda errors. if (e.response.data.errors) { response = e.response.data.errors; response = parsePUTResponseErrors(response); } else if (e.response.data.cause){ response = e.response.data.cause; } else { response = 'Unknown error occurred.'; } } else { response = 'Unknown error occurred.'; } handleNotification({ type: 'error', dismissible: true, header: "Save " + schemaName, content: (response) }); } } async function handleDeleteItemClick(e) { e.preventDefault(); showDeleteConfirmaton(); } async function handleActionsClick(e) { e.preventDefault(); let action = e.detail.id; setFocusItem(selectedItems); setAutomationAction(action); setAction('Action'); } async function handleDeleteItem(e) { e.preventDefault(); await hideDeleteConfirmaton(); let currentItem = 0; let multiReturnMessage = []; let notificationId; try { const session = await Auth.currentSession(); const apiUser = new User(session); if(selectedItems.length > 1) { notificationId = handleNotification({ type: 'success', loading: true, dismissible: false, header: "Deleting selected " + schemaName + "s..." }); } for(let item in selectedItems) { currentItem = item; await apiUser.deleteItem(selectedItems[item].wave_id, 'wave'); //Combine notifications into a single message if multi selected used, to save user dismiss clicks. if(selectedItems.length > 1){ multiReturnMessage.push(selectedItems[item].wave_name); } else { handleNotification({ type: 'success', dismissible: true, header: 'Wave deleted successfully', content: selectedItems[item].wave_name + ' was deleted.' }); } } //Create notification where multi select was used. if(selectedItems.length > 1){ handleNotification({ id: notificationId, type: 'success', dismissible: true, header: 'Waves deleted successfully', content: multiReturnMessage.join(", ") + ' were deleted.' }); } //Unselect applications marked for deletion to clear apps. setSelectedItems([]); await updateWaves(); } catch (e) { console.error(e); let response = ''; if ('response' in e && 'data' in e.response) { //Check if errors key exists from Lambda errors. if (e.response.data.errors) { response = e.response.data.errors; } else if (e.response.data.cause){ response = e.response.data.cause; } else { response = selectedItems[currentItem].wave_name + ' failed to delete with an unknown error.'; } } else { response = selectedItems[currentItem].wave_name + ' failed to delete with an unknown error.'; } handleNotification({ type: 'error', dismissible: true, header: "Wave deletion failed", content: (response) }); } } //Update actions button options on schema change. useEffect( () => { if(!props.schemaIsLoading && props.isReady){ setActions(userAutomationActionsMenuItems(props.schema, props.userEntityAccess)); } },[props.schema, props.userEntityAccess]); //Update help tools panel. useEffect(() => { if (props.schema && props.schema[schemaName].help_content) { let tempContent = props.schema[schemaName].help_content; tempContent.header = props.schema[schemaName].friendly_name ? props.schema[schemaName].friendly_name : capitalize(schemaName); props.setHelpPanelContent(tempContent) } }, [props.schema]); function provideContent(currentAction){ if(props.schemaIsLoading || !props.isReady){ return ( Loading schema... ) } switch (currentAction) { case 'Action': return ( ) case 'Add': case 'Edit': return ( ) default: return ( ) } } useEffect( () => { let selected = []; if (!isLoadingWaves) { let item = dataWaves.filter(function (entry) { return entry[itemIDKey] === params.id; }); if (item.length === 1) { selected.push(item[0]); handleItemSelectionChange(selected); //Check if URL contains edit path and switch to amend component. if (location.pathname && location.pathname.match('/edit/')) { handleEditItem(item[0]); } } else if (location.pathname && location.pathname.match('/add')) { //Add url used, redirect to add screen. handleAddItem(); } } }, [dataWaves]); return (
{provideContent(action)} {selectedItems.length === 1 ?

Are you sure you wish to delete the selected wave?

:

Are you sure you wish to delete the {selectedItems.length} selected waves?

}
Edit
); }; // Component TableView is a skeleton of a Table using AWS-UI React components. export default UserWaveTable;