diff --git a/CHANGELOG.md b/CHANGELOG.md index 45b4a5a..69727fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Avoided (infinite) display of "The list is loading. Just a moment please." for queries that should show "The result list is empty.", in cases where the user does not have the right to read the involved source(s) (#209). - Result list sorting works again and the behavior is improved - see the issue for details (#216). +- Loading values for variables ends with an error message if a required source is not available (#218). ## [1.7.0] - 2025-04-09 diff --git a/main/configs/test/config.json b/main/configs/test/config.json index f457c55..669843b 100644 --- a/main/configs/test/config.json +++ b/main/configs/test/config.json @@ -536,6 +536,24 @@ "url": "http://localhost:8080/example/index-example-file-does-not-exist", "queryLocation": "/sourceQueries/index_example_common_lt.rq" } + }, + { + "id": "9150", + "queryGroupId": "gr-test", + "queryLocation": "musicians_variables.rq", + "name": "A templated query about musicians (indirect variables) - no sources", + "description": "Test what happens when variables cannot be loaded because no source(s) available.", + "icon": "MusicNoteIcon", + "indirectVariables": { + "queryLocations": [ + "variableQueries/musicians_genre_variable.rq" + ] + }, + "comunicaContext": { + "sources": [ + "http://localhost:8080/example/favourite-musicians-file-does-not-exist" + ] + } } ] } \ No newline at end of file diff --git a/main/src/components/ErrorDisplay/ErrorDisplay.jsx b/main/src/components/ErrorDisplay/ErrorDisplay.jsx new file mode 100644 index 0000000..210877f --- /dev/null +++ b/main/src/components/ErrorDisplay/ErrorDisplay.jsx @@ -0,0 +1,31 @@ +import ReportProblemOutlinedIcon from '@mui/icons-material/ReportProblemOutlined'; +import SearchOffIcon from '@mui/icons-material/SearchOff'; +import { SvgIcon, Box } from "@mui/material"; + +const ErrorDisplay = (props) => { + const { errorMessage, searchingMessage } = props; + let icon; + let msg; + + if (errorMessage) { + icon = ReportProblemOutlinedIcon; + msg = errorMessage; + } else if (searchingMessage) { + icon = SearchOffIcon; + msg = searchingMessage; + } else { + icon = ReportProblemOutlinedIcon; + msg = "Something unexpected happened." + } + + return ( +
+ + + {msg} + +
+ ); +} + +export default ErrorDisplay; \ No newline at end of file diff --git a/main/src/components/ListResultTable/QueryResultList/QueryResultList.jsx b/main/src/components/ListResultTable/QueryResultList/QueryResultList.jsx index 3528a2b..dd0f4d6 100644 --- a/main/src/components/ListResultTable/QueryResultList/QueryResultList.jsx +++ b/main/src/components/ListResultTable/QueryResultList/QueryResultList.jsx @@ -4,9 +4,8 @@ import ActionBar from "../../ActionBar/ActionBar"; import GenericField from "../../../representationProvider/GenericField"; import TableHeader from "./TableHeader/TableHeader"; import Button from '@mui/material/Button'; -import SearchOffIcon from '@mui/icons-material/SearchOff'; -import ReportProblemOutlinedIcon from '@mui/icons-material/ReportProblemOutlined'; -import { SvgIcon, Box, Typography } from "@mui/material"; +import { Box, Typography } from "@mui/material"; +import ErrorDisplay from "../../../components/ErrorDisplay/ErrorDisplay"; import PropTypes from "prop-types"; import CustomQueryEditButton from "../../CustomQueryEditor/customQueryEditButton"; import IconProvider from "../../../IconProvider/IconProvider"; @@ -81,28 +80,14 @@ const Aside = (props) => { const NoValuesDisplay = (props) => { const { meta } = props; - let icon; - let msg; if (meta.errorMessage) { - icon = ReportProblemOutlinedIcon; - msg = `Something went wrong... ${meta.errorMessage}`; + return } else if (meta.noSources) { - icon = SearchOffIcon; - msg = "The result list is empty (no sources found)." + return } else if (meta.resultEmpty) { - icon = SearchOffIcon; - msg = "The result list is empty." + return } - - return ( -
- - - {msg} - -
- ); } const MyDatagrid = (props) => { diff --git a/main/src/components/ListResultTable/QueryResultList/TableHeader/TableHeader.jsx b/main/src/components/ListResultTable/QueryResultList/TableHeader/TableHeader.jsx index 6d16402..956ad50 100644 --- a/main/src/components/ListResultTable/QueryResultList/TableHeader/TableHeader.jsx +++ b/main/src/components/ListResultTable/QueryResultList/TableHeader/TableHeader.jsx @@ -46,7 +46,7 @@ function TableHeader({ children }) { <> *": { verticalAlign: "middle" } }} + sx={{ height: "100%", fontWeight: "bold", "& > *": { verticalAlign: "middle" } }} > {sortingAllowed ? { const navigate = useNavigate(); const query = configManager.getQueryWorkingCopyById(resource); const [waitingForVariableOptions, setWaitingForVariableOptions] = useState(!!(query.variables || query.indirectVariables)); + const [waitingForVariableOptionsError, setWaitingForVariableOptionsError] = useState(""); const [variableOptions, setVariableOptions] = useState({}); const [variableValues, setVariableValues] = useState({}); const [variablesSubmitted, setVariablesSubmitted] = useState(false); @@ -31,6 +33,7 @@ const TemplatedListResultTable = (props) => { // LOG console.log(`props: ${JSON.stringify(props, null, 2)}`); // LOG console.log(`resource: ${resource}`); // LOG console.log(`waitingForVariableOptions: ${waitingForVariableOptions}`); + // LOG console.log(`waitingForVariableOptionsError: ${waitingForVariableOptionsError}`); // LOG console.log(`variableOptions: ${JSON.stringify(variableOptions, null, 2)}`); // LOG console.log(`variableValues: ${JSON.stringify(variableValues, null, 2)}`); // LOG console.log(`variablesSubmitted: ${variablesSubmitted}`); @@ -40,10 +43,15 @@ const TemplatedListResultTable = (props) => { useEffect(() => { (async () => { if (query.variables || query.indirectVariables) { - // LOG console.log('start waiting for variable options'); - setVariableOptions(await dataProvider.getVariableOptions(query)); - // LOG console.log('done waiting for variable options'); - setWaitingForVariableOptions(false); + try { + // LOG console.log('start waiting for variable options'); + setVariableOptions(await dataProvider.getVariableOptions(query)); + // LOG console.log('done waiting for variable options'); + setWaitingForVariableOptions(false); + } catch (error) { + // LOG console.log(`error getting variable options: ${error.message}`); + setWaitingForVariableOptionsError(error.message); + } } })(); }, [resource]); @@ -55,6 +63,11 @@ const TemplatedListResultTable = (props) => { return false; } + if (waitingForVariableOptionsError) { + // LOG console.log(`TemplatedListResultTable failure while waiting for variable options: ${waitingForVariableOptionsError}`); + return ; + } + if (waitingForVariableOptions) { // LOG console.log('TemplatedListResultTable waiting for variable options.'); return ; diff --git a/main/src/dataProvider/SparqlDataProvider.js b/main/src/dataProvider/SparqlDataProvider.js index 44b5d6e..6fdb34b 100644 --- a/main/src/dataProvider/SparqlDataProvider.js +++ b/main/src/dataProvider/SparqlDataProvider.js @@ -438,11 +438,11 @@ async function getVariableOptions(query) { } } catch (error) { - throw new Error(`Error getting variable options: ${error.message}`); + throw new Error(`Error getting variable options... ${error.message}`); } if (variableOptions == {}) { - throw new Error(`The variable options are empty`); + throw new Error(`Error getting variable options... The variable options are empty`); } return variableOptions; } diff --git a/test/cypress/e2e/indirect-variables.cy.js b/test/cypress/e2e/indirect-variables.cy.js index cf4f2b7..3deeef2 100644 --- a/test/cypress/e2e/indirect-variables.cy.js +++ b/test/cypress/e2e/indirect-variables.cy.js @@ -106,6 +106,14 @@ describe("Indirect variable query", () => { }); + it("Indirect with 1 variable but no sources available to get variables from", () => { + + cy.visit("/"); + cy.contains("For testing only").click(); + cy.contains("A templated query about musicians (indirect variables) - no sources").click(); + + cy.contains("Error getting variable options...").should("exist");; + }); });