Skip to content

Commit

Permalink
Merge pull request #161 from SolidLabResearch/fix-140-save-on-pods
Browse files Browse the repository at this point in the history
Fix 140 save on pods
  • Loading branch information
EmilioTR authored Sep 17, 2024
2 parents 29a8632 + 5b1fa7f commit f66e714
Show file tree
Hide file tree
Showing 12 changed files with 667 additions and 40 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- The possibility to save and load custom queries to and from a pod (#140).

### Changed

### Fixed


## [1.3.1] - 2024-08-27

### Added
Expand Down
51 changes: 44 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ Table of contents:
* [Custom queries](#custom-queries)
* [Representation Mapper](#representation-mapper)
* [Using the local pods](#using-the-local-pods)
* [Advanced topics](#advanced-topics)
* [Adapting this project to your needs](#adapting-this-project-to-your-needs)
* [Converting custom queries into common queries](#converting-custom-queries-into-common-queries)
* [Testing](#testing)
* [Testing the production version](#testing-the-production-version)
* [Testing the development version](#testing-the-development-version)
Expand Down Expand Up @@ -137,7 +140,7 @@ The configuration file `main/src/config.json` follows a simple structure.
"description": "Description of the query",
"icon": "The key to the icon for the query. This is optional and a default menu icon will be used when left empty.",
"comunicaContext": {
"sources": "Initial list of sources over which the query should be executed",
"sources": "Initial array of sources over which the query should be executed",
"useProxy": "True or false, whether the query should be executed through the proxy or not. This field is optional and defaults to false.",
... any other field that can be used in the Comunica query engine https://comunica.dev/docs/query/advanced/context/
},
Expand Down Expand Up @@ -207,10 +210,10 @@ If all possible values for the template variables are fixed and hence can be wri
* In the config file:
* Add a `variables` object in the query's entry in the configuration file.
* In the `variables` object, for each template variable, add a property with name equal to the template variable's identifier.
* Set each such property's value to an array strings, where each string is a possible value for the corresponding template variable.
* Set each such property's value to an array of strings, where each string is a possible value for the corresponding template variable.

Note that template variables' values are not restricted to strings: URIs for example are also possible.
As a consequence, for strings the surround double quotes `"` must be added to the values in the list.
As a consequence, for strings the surround double quotes `"` must be added to the values in the array.
For URIs you must add surrounding angle brackets `<>`.
Other literals (integers for example) don't have to be surrounded with extra delimiters.
This is shown in the configuration structure above.
Expand Down Expand Up @@ -264,12 +267,16 @@ In addition, a user can create and edit custom queries, either from scratch or b
* Click "CLONE AS CUSTOM QUERY" (in a normal query) or "CLONE" (in a custom query).
* Make the desired changes in the form and click the "CREATE QUERY" button when ready. The new custom query behaves as if it were created from scratch.

* To reproduce a custom query later, a "SAVE QUERY LINK" button is provided.
* To share a custom query, a "SHARE QUERY" button is provided.
Use it to generate a unique URL for this custom query.
Visiting that URL any time later, recreates a custom query with the same specifications.
This may be useful to forward a custom query to another user.
This may be useful to share a custom query to another user or to save it for yourself.

* To clean up an unwanted custom query, there is always a button "DELETE QUERY"...
* To clean up an unwanted custom query, there is always a button "DELETE QUERY"...

**Warning**: custom queries are stored in your browser's memory and will disappear if the browser page is refreshed or when switching logins.

Logged in users however have the possibility to save/load their custom queries to/from a selectable location in their Solid pod, via the buttons in the Dashboard.

## Representation Mapper

Expand Down Expand Up @@ -314,9 +321,39 @@ You can make use of these for your own tests. Follow these steps:
These files will be available in the pod relative to `http://localhost:8080/example/`.
* Prepare the pods by executing `npm run reset:pods` in directory `test`.

## Advanced topics

### Adapting this project to your needs

The easiest way to adapt this project to your needs is:

1. Make your own fork on github.
2. Concentrate on the files in the `main` subdirectory.
3. Add your own queries in the `main/public/queries` directory and in general, your own resources in the `main/public` directory.
4. Write your own `main/src/config.json` file, following the [configuration file documentation above](#configuration-file).
5. Run or build as documented above.

### Converting custom queries into common queries

Once you have your basic configuration working, you may extend it with custom queries interactively with the query editor
and save these to a file in a pod.
You can convert such custom queries into common queries, by adding them to `main/src/config.json`.
Follow these steps to get started:

1. **Open and view the file with custom queries** using a tool, such as [Penny](https://penny.vincenttunru.com/). The file has JSON syntax and contains an array of query objects.
2. **Copy the query objects of interest** to the `"queries"` array in `main/src/config.json`.
Note that the various queries that were documented in the [configuration file documentation above](#configuration-file) in `"queryLocation"` properties,
appear here as `"queryString"` variants, with inline contents rather than references to query files (`*.rq`).
Leave as is or convert to query files as you like.
Inline queries may be hard to read due to the difficult newline coding in JSON syntax.
3. **Update the `"queryGroupId"` property** in all these queries, to separate them from the custom queries. Ensure the group exists in the `"queryGroups"` array, or create a new group if you prefer.
4. **Update the `"id"` property**, to avoid conflicts with remaining custom queries: the id must be unique and it also defines the position in the query group.
5. **Adapt any other properties** according to your preferences.
6. **Save `main/src/config.json`**, rerun or rebuild and refresh your browser to test.

## Testing

For testing we use [Cypress](https://www.cypress.io/).
For testing with the provided configuration file, we use [Cypress](https://www.cypress.io/).

> It is important to test the production version at least at the end of a development cycle.
Expand Down
12 changes: 11 additions & 1 deletion main/src/IconProvider/IconProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ import CloseIcon from '@mui/icons-material/Close';
import SettingsSuggestIcon from '@mui/icons-material/SettingsSuggest';
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
import FilterNoneIcon from '@mui/icons-material/FilterNone';
import CloudDownloadIcon from '@mui/icons-material/CloudDownload';
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
import HourglassTopIcon from '@mui/icons-material/HourglassTop';
import CreateNewFolderIcon from '@mui/icons-material/CreateNewFolder';
import FolderOffIcon from '@mui/icons-material/FolderOff';

export default {
BrushIcon,
Expand Down Expand Up @@ -51,5 +56,10 @@ export default {
CloseIcon,
SettingsSuggestIcon,
ChevronLeftIcon,
FilterNoneIcon
FilterNoneIcon,
CloudDownloadIcon,
CloudUploadIcon,
HourglassTopIcon,
CreateNewFolderIcon,
FolderOffIcon
};
1 change: 1 addition & 0 deletions main/src/authenticationProvider/authenticationProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export default {
await queryEngine.invalidateHttpCache();
const session = getDefaultSession();
await session.logout();
window.location.reload();
return false;
},
checkAuth: async function checkAuth() {
Expand Down
63 changes: 37 additions & 26 deletions main/src/components/CustomQueryEditor/customEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ import Checkbox from '@mui/material/Checkbox';
import configManager from '../../configManager/configManager';
import IconProvider from '../../IconProvider/IconProvider';

import { getDefaultSession } from "@inrupt/solid-client-authn-browser";


export default function CustomEditor(props) {
const session = getDefaultSession();
const loggedIn = session.info.isLoggedIn;

const location = useLocation();
const navigate = useNavigate();
Expand All @@ -30,7 +34,7 @@ export default function CustomEditor(props) {
});

const [showError, setShowError] = useState(false);
const [editError, setEditError] = useState(false)
const [editError, setEditError] = useState(false);
const [parsingErrorComunica, setParsingErrorComunica] = useState(false);
const [parsingErrorAsk, setParsingErrorAsk] = useState(false);
const [parsingErrorTemplate, setParsingErrorTemplate] = useState(false);
Expand Down Expand Up @@ -64,12 +68,12 @@ ORDER BY ?genre`;
"(etc...)"
],
"(etc...)": []
}, null, 5)
}, null, 5);


useEffect(() => {
try {
let searchParams
let searchParams;
if (props.newQuery) {
searchParams = new URLSearchParams(location.search);
} else {
Expand All @@ -78,16 +82,16 @@ ORDER BY ?genre`;
}
const obj = {}
searchParams.forEach((value, key) => {
obj[key] = value
obj[key] = value;
})

if (obj.indirectQueries) {
setIndirectVariableSourceList(JSON.parse(obj.indirectQueries))
setIndirectVariableSourceList(JSON.parse(obj.indirectQueries));
}
setFormData(obj)
setFormData(obj);

} catch (error) {
setEditError(true)
setEditError(true);
}
}, [location.search]);

Expand All @@ -97,12 +101,12 @@ ORDER BY ?genre`;
event.preventDefault();

if (!parsingErrorComunica && !parsingErrorAsk && !parsingErrorTemplate) {
setShowError(false)
setShowError(false);
const formData = new FormData(event.currentTarget);
const jsonData = Object.fromEntries(formData.entries());

if (jsonData.indirectVariablesCheck) {
jsonData.indirectQueries = JSON.stringify(indirectVariableSourceList)
jsonData.indirectQueries = JSON.stringify(indirectVariableSourceList);
}

const searchParams = new URLSearchParams(jsonData);
Expand All @@ -119,7 +123,7 @@ ORDER BY ?genre`;
updateQuery(jsonData, customQuery);
}
} else {
setShowError(true)
setShowError(true);
}
};

Expand All @@ -133,20 +137,20 @@ ORDER BY ?genre`;
};

const handleIndirectVariablesChange = (event, index) => {
const newList = [...indirectVariableSourceList]
newList[index] = event.target.value
setIndirectVariableSourceList(newList)
const newList = [...indirectVariableSourceList];
newList[index] = event.target.value;
setIndirectVariableSourceList(newList);
}

const handleJSONparsing = (event, errorSetter) => {
const { name, value } = event.target;
errorSetter(false)
errorSetter(false);

let parsedValue;
try {
parsedValue = JSON.parse(value);
} catch (error) {
errorSetter(true)
errorSetter(true);
parsedValue = value;
}

Expand Down Expand Up @@ -191,20 +195,20 @@ ORDER BY ?genre`;
}

if (ensureBoolean(dataWithStrings.indirectVariablesCheck)) {
parsedObject.indirectVariables = { queryStrings: JSON.parse(dataWithStrings.indirectQueries) }
parsedObject.indirectVariables = { queryStrings: JSON.parse(dataWithStrings.indirectQueries) };
}

return parsedObject;
}

// These are the functions for the addition and removal of indirect variable input fields
const handleIndirectVariableSource = () => {
setIndirectVariableSourceList([...indirectVariableSourceList, ""])
setIndirectVariableSourceList([...indirectVariableSourceList, ""]);
}
const handleIndirectVariableSourceRemove = (index) => {
const newList = [...indirectVariableSourceList];
newList.splice(index, 1);
setIndirectVariableSourceList(newList)
setIndirectVariableSourceList(newList);
}

// These Functions are the submit functions for whether the creation or edit of a custom query
Expand All @@ -218,7 +222,7 @@ ORDER BY ?genre`;
queryGroupId: "cstm",
icon: "AutoAwesomeIcon",
});
navigate(`/${creationID}`)
navigate(`/${creationID}`);
};

const updateQuery = (formData, customQuery) => {
Expand All @@ -230,11 +234,18 @@ ORDER BY ?genre`;
icon: customQuery.icon
});

navigate(`/${customQuery.id}`)
navigate(`/${customQuery.id}`);
};

return (
<React.Fragment>
{!loggedIn &&
<Card sx={{ backgroundColor: "#edaa15", padding: '16px', width: '100%' }}>
<b>Warning!</b> You are not logged in, so custom queries cannot be saved to a pod.
The SHARE QUERY button is a solution to save a custom query (as a link).
</Card>
}

<Card
component="form"
onSubmit={handleSubmit}
Expand Down Expand Up @@ -307,7 +318,7 @@ ORDER BY ?genre`;
setFormData((prevFormData) => ({
...prevFormData,
'comunicaContextCheck': !formData.comunicaContextCheck,
}))
}));
}
}

Expand Down Expand Up @@ -358,7 +369,7 @@ ORDER BY ?genre`;
setFormData((prevFormData) => ({
...prevFormData,
'sourceIndexCheck': !formData.sourceIndexCheck,
}))
}));
}
}
/>} label="Indirect sources" />
Expand Down Expand Up @@ -410,7 +421,7 @@ ORDER BY ?genre`;
setFormData((prevFormData) => ({
...prevFormData,
'directVariablesCheck': !formData.directVariablesCheck,
}))
}));
}
}
/>} label="Fixed Variables" />
Expand Down Expand Up @@ -447,7 +458,7 @@ ORDER BY ?genre`;
setFormData((prevFormData) => ({
...prevFormData,
'indirectVariablesCheck': !formData.indirectVariablesCheck,
}))
}));
}
}
/>} label="Indirect Variables" />
Expand Down Expand Up @@ -508,7 +519,7 @@ ORDER BY ?genre`;
setFormData((prevFormData) => ({
...prevFormData,
'askQueryCheck': !formData.askQueryCheck,
}))
}));
}
}
/>} label="ASK query" />
Expand Down Expand Up @@ -563,7 +574,7 @@ ORDER BY ?genre`;
</Button>

:

<Button
variant="outlined"
color='error'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export default function CustomQueryEditButton({ queryID, submitted = false }) {
setSaveOpen(true)
}}
sx={{ margin: '10px' }}>
Save Query Link
Share Query
</Button>

<Button
Expand Down
4 changes: 4 additions & 0 deletions main/src/components/Dashboard/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import CardContent from '@mui/material/CardContent';
import { Title } from 'react-admin';
import './Dashboard.css';

import SaveCustomToPod from './saveCustomQueriesToPod/saveCustomToPod';

import configManager from '../../configManager/configManager';

/**
Expand All @@ -20,6 +22,8 @@ function Dashboard() {
<Title title={title} />
<CardContent>{introductionText}</CardContent>
</Card>

<SaveCustomToPod/>
</div>
);
}
Expand Down
Loading

0 comments on commit f66e714

Please sign in to comment.