diff --git a/public/js/appChart.js b/public/js/appChart.js index d7acae3a18fd261662c3cce4bf6c46f9c1dca31f..7ff9b4a6e0685f76adc76a62f8d9dc7d129680f8 100644 --- a/public/js/appChart.js +++ b/public/js/appChart.js @@ -228,18 +228,35 @@ const drawChartUsingSelectedOptions = async function () { ) ); + // If there is an error in fetching metadata + observations (Case 1: raw observations) + // the returned array will have this structure: [[undefined, undefined], undefined] + // Note that the second element is not an array as we would expect but is a + // a single `undefined` value + if (typeof observationsRawPlusMetadataArr[0][0] === "undefined") { + throw new Error( + `There was a problem in fetching metadata and observations` + ); + } + + // If there is an error in fetching metadata + observations (Case 2: temperature difference, dT) + // a single `undefined` value instead of an array (as we would expect) will be returned + if (typeof observationsTempDiffPlusMetadataArr === "undefined") + throw new Error( + `There was a problem in calculating the temperature difference (dT).\nThis is most likely due to a problem in fetching metadata and observations` + ); + // Extract the combined arrays for observations and metadata / raw observations - const [observationsRawNestedArr, metadataTempDiffNestedArr] = + const [observationsRawNestedArr, metadataRawNestedArr] = observationsRawPlusMetadataArr; // Extract the combined arrays for observations and metadata / temperature difference (dT) - const [observationsNestedComputedArr, metadataNestedComputedArr] = + const [observationsTempDiffNestedArr, metadataTempDiffNestedArr] = observationsTempDiffPlusMetadataArr; // Create a combined array of observations and metadata const observationsPlusMetadataCombined = [ - [...observationsRawNestedArr, ...observationsNestedComputedArr], - [...metadataTempDiffNestedArr, ...metadataNestedComputedArr], + [...observationsRawNestedArr, ...observationsTempDiffNestedArr], + [...metadataRawNestedArr, ...metadataTempDiffNestedArr], ]; const [observationsComboNestedArr, metadataComboNestedArr] = @@ -384,6 +401,9 @@ const drawChartUsingSelectedOptions = async function () { } } catch (err) { console.error(err); + + // Display a dialog window with the error message + alert(err); } finally { // Enable the 'draw chart' button enableDrawChartButton(); diff --git a/public/js/src_modules/aggregateHelpers.mjs b/public/js/src_modules/aggregateHelpers.mjs index 49f2ac91742f8942c33e1dcfaf19a0aff322ffb0..e81f0cbd5d1c7011e782f4a999fb734371a2444f 100644 --- a/public/js/src_modules/aggregateHelpers.mjs +++ b/public/js/src_modules/aggregateHelpers.mjs @@ -252,58 +252,67 @@ const extractObservationValuesWithinMonthInterval = function ( samplingRate, calendarMonthStr ) { - // Extract the year and month digits from the calendar month string - const [yearNum, monthNum] = - extractMonthYearDigitsFromCalendarMonthString(calendarMonthStr); + try { + // Extract the year and month digits from the calendar month string + const [yearNum, monthNum] = + extractMonthYearDigitsFromCalendarMonthString(calendarMonthStr); - // All the months start on the first - const startDateStr = `${calendarMonthStr}-01`; + // All the months start on the first + const startDateStr = `${calendarMonthStr}-01`; - if (monthNum < 1 || monthNum > 12) { - throw new Error("The specified digit for the month of the year is invalid"); - } - // February - else if (monthNum === 2) { - // Leap year - if (checkIfLeapYear(yearNum)) { + if (monthNum < 1 || monthNum > 12) { + throw new Error( + "The specified digit for the month of the year is invalid" + ); + } + // February + else if (monthNum === 2) { + // Leap year + if (checkIfLeapYear(yearNum)) { + return extractObservationValuesWithinDatesInterval( + obsArray, + samplingRate, + startDateStr, + `${calendarMonthStr}-29` + ); + } + // Non-leap year + else { + return extractObservationValuesWithinDatesInterval( + obsArray, + samplingRate, + startDateStr, + `${calendarMonthStr}-28` + ); + } + } + // Months with 30 days + else if ( + monthNum === 4 || + monthNum === 6 || + monthNum === 9 || + monthNum === 11 + ) { return extractObservationValuesWithinDatesInterval( obsArray, samplingRate, startDateStr, - `${calendarMonthStr}-29` + `${calendarMonthStr}-30` ); } - // Non-leap year + // Months with 31 days else { return extractObservationValuesWithinDatesInterval( obsArray, samplingRate, startDateStr, - `${calendarMonthStr}-28` + `${calendarMonthStr}-31` ); } - } - // Months with 30 days - else if ( - monthNum === 4 || - monthNum === 6 || - monthNum === 9 || - monthNum === 11 - ) { - return extractObservationValuesWithinDatesInterval( - obsArray, - samplingRate, - startDateStr, - `${calendarMonthStr}-30` - ); - } - // Months with 31 days - else { - return extractObservationValuesWithinDatesInterval( - obsArray, - samplingRate, - startDateStr, - `${calendarMonthStr}-31` + } catch (err) { + // Rethrow errors from `getIndexOfStartTimestamp` or `getIndexOfEndTimestamp` functions + throw new Error( + `${err.message} \nCurrently, the monthly aggregation does not support partial calendar months` ); } }; diff --git a/public/js/src_modules/calculateTemperatureDiff.mjs b/public/js/src_modules/calculateTemperatureDiff.mjs index 615c0238a117fca5463000d8cf3dfddacfa81cd4..b63dc94260e557d4e45d93f5cb519127ef3f2a55 100644 --- a/public/js/src_modules/calculateTemperatureDiff.mjs +++ b/public/js/src_modules/calculateTemperatureDiff.mjs @@ -175,6 +175,16 @@ export const calculateVorlaufMinusRuecklaufTemperature = async function ( bldgDataPtSamplingRateNestedArr ); + // If there is an error in fetching metadata + observations, + // the returned array will have this structure: [[undefined, undefined], undefined] + // Note that the second element is not an array as we would expect but is a + // a single `undefined` value + if (typeof observationsPlusMetadata[0][0] === "undefined") { + throw new Error( + `There was a problem in calculating the temperature difference (dT).\nThis is most likely due to a problem in fetching metadata and observations` + ); + } + // dT observations (timestamp + value) const vorlaufMinusRuecklaufTemperatureObs = calculateVorlaufMinusRuecklaufTemperatureObservations( diff --git a/public/js/src_modules/fetchData.mjs b/public/js/src_modules/fetchData.mjs index 9064ed667435dffc55dcc2d7faa5bf408a385f63..2d6cbabab03a296b0d34fb96846f341511cd535f 100644 --- a/public/js/src_modules/fetchData.mjs +++ b/public/js/src_modules/fetchData.mjs @@ -24,44 +24,73 @@ const createObservationsUrl = function (baseUrl, datastreamID) { return `${baseUrl}/Datastreams(${datastreamID})/Observations`; }; +/** + * Perform a GET request to fetch a single Datastream using the Axios library + * + * @param {String} urlDatastream A URL that fetches a single Datastream from an STA instance + * @returns {Promise} A promise that contains the Datastream metadata when fulfilled + */ +const getDatastream = function (urlDatastream) { + return axios.get(urlDatastream); +}; + /** * Perform a GET request using the Axios library * @param {String} urlObservations A URL that fetches Observations from an STA instance * @param {Object} urlParamObj The URL parameters to be sent together with the GET request * @returns {Promise} A promise that contains the first page of results when fulfilled */ -const performGetRequestUsingAxios = function (urlObservations, urlParamObj) { +const getObservations = function (urlObservations, urlParamObj) { return axios.get(urlObservations, { params: urlParamObj, }); }; /** - * Retrieve the metadata for a single datastream + * Extract the metadata from a single datastream * @async * @param {String} urlDatastream A URL that fetches a Datastream from an STA instance * @returns {Promise} A promise that contains a metadata object for a Datastream when fulfilled */ -const getMetadataFromSingleDatastream = async function (urlDatastream) { +const extractMetadataFromSingleDatastream = async function (urlDatastream) { try { // Extract properties of interest const { data: { description, name, unitOfMeasurement }, - } = await performGetRequestUsingAxios(urlDatastream); + } = await getDatastream(urlDatastream); return { description, name, unitOfMeasurement }; } catch (err) { - console.error(err); + // Server responded with status code outside of 2xx range + if (err.response) { + throw new Error( + `The request to fetch Datastream metadata was made but the server responded with: \n${err.message}` + ); + } + // Request was made but no response was received + else if (err.request) { + throw new Error( + `The request to fetch Datastream metadata was made but no response was received: \n${err.message}` + ); + } + // Problem with setting up the request + else { + throw new Error( + `There was a problem setting up the request to fetch Datastream metadata: \n${err.message}` + ); + } } }; /** - * Retrieve metadata from multiple datastreams + * Extract the metadata from multiple datastreams * @async * @param {Array} datastreamsUrlArr An array that contains N Datastream URL strings * @returns {Promise} A promise that contains an array of N Datastream metadata objects when fulfilled */ -const getMetadataFromMultipleDatastreams = async function (datastreamsUrlArr) { +const extractMetadataFromMultipleDatastreams = async function ( + datastreamsUrlArr +) { try { // Array to store our final result const datastreamMetadataArr = []; @@ -69,7 +98,7 @@ const getMetadataFromMultipleDatastreams = async function (datastreamsUrlArr) { // Use for/of loop - we need to maintain the order of execution of the async operations for (const datastreamUrl of datastreamsUrlArr) { // Metadata from a single Datastream - const datastreamMetadata = await getMetadataFromSingleDatastream( + const datastreamMetadata = await extractMetadataFromSingleDatastream( datastreamUrl ); datastreamMetadataArr.push(datastreamMetadata); @@ -109,7 +138,24 @@ const combineResultsFromAllPages = async function (httpGetRequestPromise) { return lastSuccess; } } catch (err) { - console.error(err); + // Server responded with status code outside of 2xx range + if (err.response) { + throw new Error( + `The request to fetch Observations was made but the server responded with: \n${err.message}` + ); + } + // Request was made but no response was received + else if (err.request) { + throw new Error( + `The request to fetch Observations was made but no response was received: \n${err.message}` + ); + } + // Problem with setting up the request + else { + throw new Error( + `There was a problem setting up the request to fetch Observations: \n${err.message}` + ); + } } }; @@ -208,7 +254,7 @@ const getMetadataPlusObservationsFromSingleOrMultipleDatastreams = // Promise objects - Observations const observationsPromisesArr = observationsUrlArr.map((obsUrl) => extractCombinedObservationsFromAllPages( - performGetRequestUsingAxios(obsUrl, urlParamObj) + getObservations(obsUrl, urlParamObj) ) ); @@ -218,7 +264,7 @@ const getMetadataPlusObservationsFromSingleOrMultipleDatastreams = ); // Metadata array - const metadataArr = await getMetadataFromMultipleDatastreams( + const metadataArr = await extractMetadataFromMultipleDatastreams( datastreamsUrlArr );