Skip to content

Commit 8bc4ebd

Browse files
authored
Isolated comunica engine in wrapper class (#153)
Isolated comunica engine in wrapper class. Fixes #153.
1 parent 5f7f1fa commit 8bc4ebd

File tree

6 files changed

+243
-180
lines changed

6 files changed

+243
-180
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
### Added
10+
### Added
1111

1212
### Changed
1313

14+
- Refactoring: isolated the Comunica engine with accompanying operations into a wrapper class (#152).
15+
1416
### Fixed
1517

1618
- Correct error display when the queryLocation is a non existing file or faultive (#147).

src/authenticationProvider/authenticationProvider.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import {
77
} from "@inrupt/solid-client";
88
import { getDefaultSession, fetch } from "@inrupt/solid-client-authn-browser";
99
import { FOAF } from "@inrupt/vocab-common-rdf";
10-
import SparqlDataProvider from "./../dataProvider/SparqlDataProvider";
10+
11+
import comunicaEngineWrapper from "../comunicaEngineWrapper/comunicaEngineWrapper";
12+
1113

1214
const queryEngine = new QueryEngine();
1315

@@ -40,7 +42,7 @@ export default {
4042
}
4143
},
4244
logout: async function logout() {
43-
await SparqlDataProvider.queryEngine.invalidateHttpCache();
45+
await comunicaEngineWrapper.reset();
4446
await queryEngine.invalidateHttpCache();
4547
const session = getDefaultSession();
4648
await session.logout();

src/components/ActionBar/SourceFetchStatusIcon/SourceFetchStatusIcon.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import RemoveCircleOutlineIcon from '@mui/icons-material/RemoveCircleOutline';
44
import { Tooltip } from "@mui/material";
55
import PropTypes from "prop-types";
66
import { Component } from "react";
7+
import comunicaEngineWrapper from '../../../comunicaEngineWrapper/comunicaEngineWrapper';
78

89
/**
910
* @param {object} props - the props passed to the component
@@ -17,9 +18,9 @@ function SourceFetchStatusIcon({ context, source, proxyUrl }) {
1718
if (context.useProxy) {
1819
actualSource = `${proxyUrl}${source}`;
1920
}
20-
const status = context.fetchStatusNumber[actualSource];
21+
const status = comunicaEngineWrapper.getFetchStatusNumber(actualSource);
2122

22-
if (context.fetchSuccess[actualSource]) {
23+
if (comunicaEngineWrapper.getFetchSuccess(actualSource)) {
2324
return (
2425
<Tooltip title="Fetch was successful">
2526
<CheckIcon size="small" />

src/components/ActionBar/SourceVerificationIcon/SourceVerificationIcon.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import GppGoodIcon from '@mui/icons-material/GppGood';
77
import GppBadIcon from '@mui/icons-material/GppBad';
88
import GppMaybeIcon from '@mui/icons-material/GppMaybe';
99
import myVerify from '../../../../src/vendor/verify';
10+
import comunicaEngineWrapper from '../../../comunicaEngineWrapper/comunicaEngineWrapper';
1011

1112
const VERIFICATION_STATES = {
1213
VERIFIED: 'VERIFIED',
@@ -62,7 +63,7 @@ function SourceVerificationIcon({ context, source, proxyUrl }) {
6263
*/
6364
function verify() {
6465
setNeedsVerification(true);
65-
verifyFunction(sourceUrl, context.underlyingFetchFunction).then((result) => {
66+
verifyFunction(sourceUrl, comunicaEngineWrapper.getUnderlyingFetchFunction()).then((result) => {
6667
setVerificationState(result);
6768
setIsLoading(false);
6869
})
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import { QueryEngine } from "@comunica/query-sparql";
2+
import {
3+
getDefaultSession,
4+
fetch as authFetch,
5+
} from "@inrupt/solid-client-authn-browser";
6+
7+
/**
8+
* A class wrapping a Comunica engine, used for all but login actions.
9+
*/
10+
class ComunicaEngineWrapper {
11+
12+
_engine;
13+
_fetchSuccess;
14+
_fetchStatusNumber;
15+
_underlyingFetchFunction;
16+
17+
constructor() {
18+
this._engine = new QueryEngine();
19+
this._fetchSuccess = {};
20+
this._fetchStatusNumber = {};
21+
this._underlyingFetchFunction = undefined;
22+
}
23+
24+
/**
25+
* Resets the engine and all we maintained here about executed queries
26+
*/
27+
reset() {
28+
this._engine.invalidateHttpCache();
29+
this._fetchSuccess = {};
30+
this._fetchStatusNumber = {};
31+
this._underlyingFetchFunction = undefined;
32+
}
33+
34+
getFetchSuccess(arg) {
35+
return this._fetchSuccess[arg];
36+
}
37+
38+
getFetchStatusNumber(arg) {
39+
return this._fetchStatusNumber[arg];
40+
}
41+
42+
getUnderlyingFetchFunction() {
43+
return this._underlyingFetchFunction;
44+
}
45+
46+
/**
47+
* Executes one generic SPARQL query with the Comunica engine
48+
*
49+
* Support the following callback functions. Forward only the ones you need.
50+
* - "variables": will be called once with an array of variable names, in case of a SELECT query
51+
* - "bindings": will be called for every bindings combo, in case of a SELECT query
52+
* - "quads": will be called for every quad, in case of a CONSTRUCT query
53+
* - "boolean": will be called for the resulting boolean, in case of an ASK query
54+
*
55+
* @param {string} queryText - the SPARQL query text
56+
* @param {object} context - the context to provide to the Comunica engine
57+
* @param {object} callbacks - an object contains the callback functions you specify
58+
* @returns {void} when the query has finished
59+
*/
60+
async query(queryText, context, callbacks) {
61+
try {
62+
this._prepareQuery(context);
63+
let result = await this._engine.query(queryText, context);
64+
switch (result.resultType) {
65+
case 'bindings':
66+
const metadata = await result.metadata();
67+
const variables = metadata.variables.map((val) => {
68+
return val.value;
69+
});
70+
if (callbacks["variables"]) {
71+
callbacks["variables"](variables);
72+
}
73+
const bindingsStream = await result.execute();
74+
await new Promise((resolve, reject) => {
75+
if (callbacks["bindings"]) {
76+
bindingsStream.on('data', (bindings) => {
77+
callbacks["bindings"](bindings);
78+
});
79+
}
80+
bindingsStream.on('end', resolve);
81+
bindingsStream.on('error', reject);
82+
});
83+
break;
84+
case 'quads':
85+
const quadStream = await result.execute();
86+
await new Promise((resolve, reject) => {
87+
if (callbacks["quads"]) {
88+
quadStream.on('data', (quad) => {
89+
callbacks["quads"](quad);
90+
});
91+
}
92+
quadStream.on('end', resolve);
93+
quadStream.on('error', reject);
94+
});
95+
break;
96+
case 'boolean':
97+
const answer = await result.execute();
98+
if (callbacks["boolean"]) {
99+
callbacks["boolean"](answer);
100+
}
101+
break;
102+
default:
103+
break;
104+
}
105+
} catch (error) {
106+
this.reset();
107+
throw error;
108+
}
109+
}
110+
111+
/**
112+
* Executes one SPARQL SELECT query with the Comunica engine
113+
*
114+
* @param {string} queryText - the SPARQL SELECT query text
115+
* @param {object} context - the context to provide to the Comunica engine
116+
* @returns {Promise <BindingsStream>} Promis to the bindings stream
117+
*/
118+
async queryBindings(queryText, context) {
119+
try {
120+
this._prepareQuery(context);
121+
return this._engine.queryBindings(queryText, context);
122+
} catch (error) {
123+
this.reset();
124+
throw error;
125+
}
126+
}
127+
128+
/**
129+
* Prepares a call to any engine's query function
130+
*
131+
* @param {object} context - the context that will be used
132+
*/
133+
_prepareQuery(context) {
134+
// avoid faulty fetch status for sources cached in Comunica
135+
for (const source of context.sources) {
136+
this._fetchSuccess[source] = true;
137+
}
138+
this._underlyingFetchFunction = fetch;
139+
if (getDefaultSession().info.isLoggedIn) {
140+
this._underlyingFetchFunction = authFetch;
141+
}
142+
context.fetch = ComunicaEngineWrapper._getWrappedFetchFunction(this._underlyingFetchFunction, this);
143+
}
144+
145+
/**
146+
* Returns a function that wraps the underlying fetch function and sets the fetch success information in member variables of _this.
147+
*
148+
* @param underlyingFetchFunction - the underlying fetch functin
149+
* @param {ComunicaEngineWrapper} _this - the calling ComunicaEngineWrapper object
150+
* @returns {Function} that function.
151+
*/
152+
static _getWrappedFetchFunction(underlyingFetchFunction, _this) {
153+
const wrappedFetchFunction = async (arg) => {
154+
try {
155+
const response = await underlyingFetchFunction(arg, {
156+
headers: {
157+
Accept: "application/n-quads,application/trig;q=0.9,text/turtle;q=0.8,application/n-triples;q=0.7,*/*;q=0.1"
158+
}
159+
});
160+
_this._fetchSuccess[arg] = response.ok;
161+
_this._fetchStatusNumber[arg] = response.status;
162+
return response;
163+
}
164+
catch (error) {
165+
_this._fetchSuccess[arg] = false;
166+
throw error;
167+
}
168+
}
169+
170+
return wrappedFetchFunction;
171+
}
172+
}
173+
174+
const comunicaEngineWrapper = new ComunicaEngineWrapper();
175+
export default comunicaEngineWrapper;

0 commit comments

Comments
 (0)