From 4085b14f4de90b0db5b4d03b45a92bda01a23cf1 Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Tue, 13 Aug 2024 16:24:18 -0400 Subject: [PATCH 1/5] looking into the date picker validation --- .../trays/model-selection/synopticTab.js | 117 +++++++++++++----- 1 file changed, 89 insertions(+), 28 deletions(-) diff --git a/src/components/trays/model-selection/synopticTab.js b/src/components/trays/model-selection/synopticTab.js index fea8455d..03febde1 100644 --- a/src/components/trays/model-selection/synopticTab.js +++ b/src/components/trays/model-selection/synopticTab.js @@ -25,7 +25,7 @@ export const SynopticTabForm = () => { // init the data urls const rootUrl = `${ getNamespacedEnvParam('REACT_APP_UI_DATA_URL') }`; - const basePulldownUrl = `get_pulldown_data?met_class=synoptic&use_v3_sp=true${ getBrandingHandler() }`; + const basePulldownUrl = `get_pulldown_data?met_class=synoptic${ getBrandingHandler() }`; const baseDataUrl = `get_ui_data_secure?limit=14&met_class=synoptic&use_v3_sp=true${ getBrandingHandler() }`; const [finalDataUrl, setFinalDataUrl] = useState(rootUrl + basePulldownUrl); @@ -33,6 +33,22 @@ export const SynopticTabForm = () => { const [dropDownData, setDropDownData] = useState(null); const [catalogData, setCatalogData] = useState(null); + const setChangedSynopticDate = (newValue) => { + // if there was a valid date + if (!isNaN(newValue) && newValue !== null) { + console.log('Good date: ' + newValue.$d); + // save the date + setSynopticDate(newValue.$d); + } + // else handle a bad date + else { + console.log('Bad date: ' + newValue); + // clear the date + // setSynopticDate(null); + setError('Invalid Date.'); + } + }; + /** * method to initiate a model search with the filter selections on the synoptic form * @@ -79,27 +95,43 @@ export const SynopticTabForm = () => { }; // make the call to get the data - const {data} = await axios.get(finalDataUrl, requestOptions); + const ret_val = await axios + // make the call to get the data + .get(finalDataUrl, requestOptions) + // use the data returned + .then (( response ) => { + // return the data + return response.data; + }) + .catch (( error ) => { + // make sure we do not render anything + return error.response.status; + }); - // check the request type - if (finalDataUrl.indexOf('get_pulldown_data') !== -1) { - // save the dropdown data - setDropDownData(data); - } - else { - // save the catalog data - setCatalogData(data); + // if there was an error from the web service + if (ret_val !== 500) { + // check the request type to save it to the correct place + if (finalDataUrl.indexOf('get_pulldown_data') !== -1) { + console.log('finalDataUrl: ' + finalDataUrl); + + // save the dropdown data + setDropDownData(ret_val); + } + else { + // save the catalog data + setCatalogData(ret_val); + } } + else + resetForm(); // return something return true; - }, - - refetchOnWindowFocus: false + }, refetchOnWindowFocus: false }); /** - * this will build the data url and will cause a DB hit + * this will build the data url and will cause a web service/DB hit */ useEffect(() => { // build the new data url @@ -163,49 +195,78 @@ export const SynopticTabForm = () => { return !dropDownData['run_dates'].includes(date.toISOString().split("T")[0]); }; + const [error, setError] = React.useState(null); + + const errorMessage = React.useMemo(() => { + switch (error) { + case 'minDate': { + return 'Please select a date after 01/01/2020'; + } + + case 'invalidDate': { + return 'Your date is not valid'; + } + + default: { + return ''; + } + } + }, [error]); + /** * return the rendered component */ return ( -
- - + + + setError(newError)} + //disableFuture name="synoptic-date" - shouldDisableDate={disableDate} - slotProps={{textField: {size: 'small'}, field: {clearable: true}}} + shouldDisableDate={ disableDate } + //minDate={ new Date(2020, 0, 1) } + slotProps={{ + textField: { size: 'small' }, + helperText: errorMessage, + field: { clearable: true }, + actionBar: { actions: ['clear'] }, + }} + onChange={(newValue) => { - setSynopticDate(newValue.$d); + setChangedSynopticDate(newValue); }}/> - { setSynopticCycle(newValue); }}> - + - { setSynopticGrid(newValue); }}> - + - { setSynopticInstance(newValue); }}> - + - + - {} + { }
From f55cadd1427efe05239d353fa503192b83d099a1 Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Wed, 14 Aug 2024 09:45:22 -0400 Subject: [PATCH 2/5] upping to version of axios to handle the SSRF vulnerability issue --- src/components/trays/model-selection/synopticTab.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/trays/model-selection/synopticTab.js b/src/components/trays/model-selection/synopticTab.js index 03febde1..af21ec52 100644 --- a/src/components/trays/model-selection/synopticTab.js +++ b/src/components/trays/model-selection/synopticTab.js @@ -199,6 +199,10 @@ export const SynopticTabForm = () => { const errorMessage = React.useMemo(() => { switch (error) { + case 'maxDate': { + return 'Please select a date that is not in the future'; + } + case 'minDate': { return 'Please select a date after 01/01/2020'; } @@ -223,15 +227,14 @@ export const SynopticTabForm = () => { setError(newError)} - //disableFuture + disableFuture name="synoptic-date" shouldDisableDate={ disableDate } - //minDate={ new Date(2020, 0, 1) } + maxDate={ new Date(2020, 0, 1) } slotProps={{ - textField: { size: 'small' }, - helperText: errorMessage, + textField: { size: 'small', helperText: errorMessage}, field: { clearable: true }, actionBar: { actions: ['clear'] }, }} From 093a08826f5a9b7e7a4a84a0583f1a93be531f2c Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:23:00 -0400 Subject: [PATCH 3/5] Adding functionality to handle date picker usage/resetting --- .../trays/model-selection/synopticTab.js | 55 +++++++++++++------ 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/src/components/trays/model-selection/synopticTab.js b/src/components/trays/model-selection/synopticTab.js index af21ec52..7bfd7ac2 100644 --- a/src/components/trays/model-selection/synopticTab.js +++ b/src/components/trays/model-selection/synopticTab.js @@ -1,4 +1,4 @@ -import React, { Fragment, useState, useEffect } from 'react'; +import React, { Fragment, useState, useEffect, useMemo } from 'react'; import { Button, Divider, Select, Stack } from '@mui/joy'; import { DatePicker } from '@mui/x-date-pickers'; import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; @@ -8,6 +8,7 @@ import axios from 'axios'; import DropDownOptions from "@model-selection/DropDownOptions"; import CatalogItems from "@model-selection/catalogItems"; import { getNamespacedEnvParam, getBrandingHandler } from "@utils/map-utils"; +import dayjs from 'dayjs'; /** * Form to filter/select synoptic runs @@ -18,7 +19,7 @@ import { getNamespacedEnvParam, getBrandingHandler } from "@utils/map-utils"; export const SynopticTabForm = () => { // declare all state variables for the synoptic tab dropdown data - const [synopticDate, setSynopticDate] = useState(null); + const [synopticDate, setSynopticDate] = useState(dayjs(new Date())); const [synopticCycle, setSynopticCycle] = useState(null); const [synopticGrid, setSynopticGrid] = useState(null); const [synopticInstance, setSynopticInstance] = useState(null); @@ -33,19 +34,21 @@ export const SynopticTabForm = () => { const [dropDownData, setDropDownData] = useState(null); const [catalogData, setCatalogData] = useState(null); + // state for the date validation error + const [error, setError] = useState(null); + const setChangedSynopticDate = (newValue) => { // if there was a valid date if (!isNaN(newValue) && newValue !== null) { - console.log('Good date: ' + newValue.$d); + console.log('setChangedSynopticDate Good date: ' + newValue.$d + ', error: ' + error + ', newValue: ' + newValue); // save the date setSynopticDate(newValue.$d); } // else handle a bad date else { - console.log('Bad date: ' + newValue); + console.log('setChangedSynopticDate Bad date: ' + newValue); // clear the date - // setSynopticDate(null); - setError('Invalid Date.'); + setSynopticDate(null); } }; @@ -58,6 +61,8 @@ export const SynopticTabForm = () => { // avoid doing the usual form submit operations event.preventDefault(); + console.log('formSynopticSubmit error: ' + error); + // build the query string from the submitted form data let queryString = ((synopticDate) ? '&run_date=' + synopticDate.toISOString() : '') + @@ -108,12 +113,13 @@ export const SynopticTabForm = () => { return error.response.status; }); + if (finalDataUrl.indexOf('get_pulldown_data') !== -1) + console.log('finalDataUrl: ' + finalDataUrl); + // if there was an error from the web service if (ret_val !== 500) { // check the request type to save it to the correct place if (finalDataUrl.indexOf('get_pulldown_data') !== -1) { - console.log('finalDataUrl: ' + finalDataUrl); - // save the dropdown data setDropDownData(ret_val); } @@ -123,6 +129,7 @@ export const SynopticTabForm = () => { } } else + // reset the form data resetForm(); // return something @@ -191,13 +198,20 @@ export const SynopticTabForm = () => { * @returns {boolean} */ const disableDate = (date) => { - // return false if the date is not found in the list of available dates - return !dropDownData['run_dates'].includes(date.toISOString().split("T")[0]); + if (dropDownData) { + // return false if the date is not found in the list of available dates + return !dropDownData['run_dates'].includes(date.toISOString().split("T")[0]); + } }; - const [error, setError] = React.useState(null); + /** + * handles date picker error text + * + * @type {string} + */ + const errorMessage = useMemo(() => { + console.log('errorMessage error: ' + error); - const errorMessage = React.useMemo(() => { switch (error) { case 'maxDate': { return 'Please select a date that is not in the future'; @@ -208,13 +222,18 @@ export const SynopticTabForm = () => { } case 'invalidDate': { - return 'Your date is not valid'; + return 'This date is not yet valid'; + } + + case 'shouldDisableDate': { + return 'There is no data available on this date'; } default: { return ''; } } + }, [error]); /** @@ -226,13 +245,12 @@ export const SynopticTabForm = () => { setError(newError)} + defaultValue={ dayjs(new Date()) } + onError={ (newError) => setError(newError) } disableFuture name="synoptic-date" shouldDisableDate={ disableDate } - maxDate={ new Date(2020, 0, 1) } + minDate={ dayjs('2023-06-01') } slotProps={{ textField: { size: 'small', helperText: errorMessage}, field: { clearable: true }, @@ -242,6 +260,7 @@ export const SynopticTabForm = () => { onChange={(newValue) => { setChangedSynopticDate(newValue); }}/> + - + From 895729c84821e3d45c23e1ead85bacf455f4b1fe Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:23:29 -0400 Subject: [PATCH 4/5] adding data error condition when no catalog data is retrieved. --- src/components/trays/model-selection/catalogItems.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/trays/model-selection/catalogItems.js b/src/components/trays/model-selection/catalogItems.js index 1f281a5f..a668f557 100644 --- a/src/components/trays/model-selection/catalogItems.js +++ b/src/components/trays/model-selection/catalogItems.js @@ -135,6 +135,14 @@ export default function CatalogItems(data) { ); } + else if(data.data['catalog'] === undefined || data.data['catalog'] === null) { + return ( +
+ Error: { 'No catalog data retrieved.' } +
+ ); + } + // return all the data cards else { // save the name of the element for tropical storms and advisory numbers From 28bcd8a4c5ab20ecc5b412e1f84d3ac071ccddcc Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:36:41 -0400 Subject: [PATCH 5/5] removing console logs --- src/components/trays/model-selection/synopticTab.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/trays/model-selection/synopticTab.js b/src/components/trays/model-selection/synopticTab.js index 7bfd7ac2..f5dbc481 100644 --- a/src/components/trays/model-selection/synopticTab.js +++ b/src/components/trays/model-selection/synopticTab.js @@ -40,13 +40,13 @@ export const SynopticTabForm = () => { const setChangedSynopticDate = (newValue) => { // if there was a valid date if (!isNaN(newValue) && newValue !== null) { - console.log('setChangedSynopticDate Good date: ' + newValue.$d + ', error: ' + error + ', newValue: ' + newValue); + //console.log('setChangedSynopticDate Good date: ' + newValue.$d + ', error: ' + error + ', newValue: ' + newValue); // save the date setSynopticDate(newValue.$d); } // else handle a bad date else { - console.log('setChangedSynopticDate Bad date: ' + newValue); + //console.log('setChangedSynopticDate Bad date: ' + newValue); // clear the date setSynopticDate(null); } @@ -61,7 +61,7 @@ export const SynopticTabForm = () => { // avoid doing the usual form submit operations event.preventDefault(); - console.log('formSynopticSubmit error: ' + error); + //console.log('formSynopticSubmit error: ' + error); // build the query string from the submitted form data let queryString = @@ -113,8 +113,8 @@ export const SynopticTabForm = () => { return error.response.status; }); - if (finalDataUrl.indexOf('get_pulldown_data') !== -1) - console.log('finalDataUrl: ' + finalDataUrl); + // if (finalDataUrl.indexOf('get_pulldown_data') !== -1) + // console.log('finalDataUrl: ' + finalDataUrl); // if there was an error from the web service if (ret_val !== 500) { @@ -210,7 +210,7 @@ export const SynopticTabForm = () => { * @type {string} */ const errorMessage = useMemo(() => { - console.log('errorMessage error: ' + error); + //console.log('errorMessage error: ' + error); switch (error) { case 'maxDate': {