rdflib 2.2.35-0f0440d9 → 2.2.35-2bb9ed0b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/rdflib.min.js +1 -1
- package/dist/rdflib.min.js.LICENSE.txt +0 -2
- package/dist/rdflib.min.js.map +1 -1
- package/esm/blank-node.js +57 -82
- package/esm/class-order.js +1 -1
- package/esm/collection.js +70 -103
- package/esm/default-graph.js +13 -30
- package/esm/empty.js +8 -23
- package/esm/factories/canonical-data-factory.js +33 -29
- package/esm/factories/extended-term-factory.js +18 -13
- package/esm/factories/factory-types.js +1 -1
- package/esm/factories/rdflib-data-factory.js +9 -11
- package/esm/fetcher.js +1366 -1651
- package/esm/formula.js +631 -736
- package/esm/index.js +31 -48
- package/esm/jsonldparser.js +19 -26
- package/esm/jsonparser.js +1 -1
- package/esm/lists.js +38 -86
- package/esm/literal.js +120 -154
- package/esm/log.js +7 -7
- package/esm/n3parser.js +1008 -1085
- package/esm/named-node.js +69 -96
- package/esm/namespace.js +2 -4
- package/esm/node-internal.js +73 -96
- package/esm/node.js +1 -1
- package/esm/parse.js +3 -3
- package/esm/patch-parser.js +1 -1
- package/esm/query.js +15 -16
- package/esm/rdfaparser.js +775 -841
- package/esm/rdfxmlparser.js +348 -364
- package/esm/serialize.js +2 -2
- package/esm/serializer.js +835 -877
- package/esm/statement.js +52 -71
- package/esm/store.js +853 -957
- package/esm/types.js +21 -21
- package/esm/update-manager.js +964 -1100
- package/esm/updates-via.js +104 -132
- package/esm/uri.js +3 -3
- package/esm/utils/default-graph-uri.js +2 -2
- package/esm/utils/terms.js +4 -5
- package/esm/utils-js.js +5 -5
- package/esm/utils.js +6 -6
- package/esm/variable.js +32 -55
- package/esm/xsd.js +2 -2
- package/lib/blank-node.js +57 -80
- package/lib/class-order.js +1 -1
- package/lib/collection.js +70 -101
- package/lib/default-graph.js +14 -29
- package/lib/empty.js +9 -22
- package/lib/factories/canonical-data-factory.js +35 -31
- package/lib/factories/extended-term-factory.js +18 -13
- package/lib/factories/factory-types.js +1 -1
- package/lib/factories/rdflib-data-factory.js +9 -11
- package/lib/fetcher.js +1375 -1654
- package/lib/formula.js +632 -735
- package/lib/index.js +80 -84
- package/lib/jsonldparser.js +21 -32
- package/lib/jsonparser.js +1 -1
- package/lib/lists.js +47 -87
- package/lib/literal.js +121 -153
- package/lib/log.js +7 -7
- package/lib/n3parser.js +1011 -1089
- package/lib/named-node.js +70 -95
- package/lib/namespace.js +2 -4
- package/lib/node-internal.js +73 -94
- package/lib/node.js +1 -1
- package/lib/parse.js +5 -6
- package/lib/patch-parser.js +1 -1
- package/lib/query.js +20 -18
- package/lib/rdfaparser.js +778 -843
- package/lib/rdfxmlparser.js +351 -365
- package/lib/serialize.js +2 -2
- package/lib/serializer.js +840 -880
- package/lib/statement.js +55 -73
- package/lib/store.js +860 -958
- package/lib/types.js +21 -21
- package/lib/update-manager.js +969 -1102
- package/lib/updates-via.js +107 -132
- package/lib/uri.js +3 -3
- package/lib/utils/default-graph-uri.js +2 -2
- package/lib/utils/terms.js +4 -6
- package/lib/utils-js.js +8 -9
- package/lib/utils.js +6 -6
- package/lib/variable.js +35 -57
- package/lib/xsd.js +2 -2
- package/package.json +1 -1
- package/src/serializer.js +1 -1
package/lib/update-manager.js
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
var _typeof3 = require("@babel/runtime/helpers/typeof");
|
|
5
4
|
Object.defineProperty(exports, "__esModule", {
|
|
6
5
|
value: true
|
|
7
6
|
});
|
|
8
7
|
exports.default = void 0;
|
|
9
|
-
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
10
|
-
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
|
|
11
|
-
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
12
|
-
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
13
|
-
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
14
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
15
9
|
var _store = _interopRequireDefault(require("./store"));
|
|
16
10
|
var _uri = require("./uri");
|
|
@@ -20,28 +14,26 @@ var _serializer = _interopRequireDefault(require("./serializer"));
|
|
|
20
14
|
var _terms = require("./utils/terms");
|
|
21
15
|
var Util = _interopRequireWildcard(require("./utils-js"));
|
|
22
16
|
var _termValue = require("./utils/termValue");
|
|
23
|
-
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function
|
|
24
|
-
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" !=
|
|
25
|
-
|
|
26
|
-
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
27
|
-
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } /* @file Update Manager Class
|
|
17
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
18
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
19
|
+
/* @file Update Manager Class
|
|
28
20
|
**
|
|
29
21
|
** 2007-07-15 original SPARQL Update module by Joe Presbrey <presbrey@mit.edu>
|
|
30
22
|
** 2010-08-08 TimBL folded in Kenny's WEBDAV
|
|
31
23
|
** 2010-12-07 TimBL added local file write code
|
|
32
24
|
*/
|
|
25
|
+
|
|
33
26
|
/**
|
|
34
27
|
* The UpdateManager is a helper object for a store.
|
|
35
28
|
* Just as a Fetcher provides the store with the ability to read and write,
|
|
36
29
|
* the Update Manager provides functionality for making small patches in real time,
|
|
37
30
|
* and also looking out for concurrent updates from other agents
|
|
38
31
|
*/
|
|
39
|
-
|
|
32
|
+
class UpdateManager {
|
|
40
33
|
/**
|
|
41
34
|
* @param store - The quadstore to store data and metadata. Created if not passed.
|
|
42
35
|
*/
|
|
43
|
-
|
|
44
|
-
(0, _classCallCheck2.default)(this, UpdateManager);
|
|
36
|
+
constructor(store) {
|
|
45
37
|
(0, _defineProperty2.default)(this, "store", void 0);
|
|
46
38
|
(0, _defineProperty2.default)(this, "ifps", void 0);
|
|
47
39
|
(0, _defineProperty2.default)(this, "fps", void 0);
|
|
@@ -71,1206 +63,1081 @@ var UpdateManager = exports.default = /*#__PURE__*/function () {
|
|
|
71
63
|
this.ns.owl = (0, _namespace.default)('http://www.w3.org/2002/07/owl#');
|
|
72
64
|
this.patchControl = [];
|
|
73
65
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (!this.patchControl[doc.value]) {
|
|
78
|
-
this.patchControl[doc.value] = [];
|
|
79
|
-
}
|
|
80
|
-
return this.patchControl[doc.value];
|
|
81
|
-
}
|
|
82
|
-
}, {
|
|
83
|
-
key: "isHttpUri",
|
|
84
|
-
value: function isHttpUri(uri) {
|
|
85
|
-
return uri.slice(0, 4) === 'http';
|
|
66
|
+
patchControlFor(doc) {
|
|
67
|
+
if (!this.patchControl[doc.value]) {
|
|
68
|
+
this.patchControl[doc.value] = [];
|
|
86
69
|
}
|
|
70
|
+
return this.patchControl[doc.value];
|
|
71
|
+
}
|
|
72
|
+
isHttpUri(uri) {
|
|
73
|
+
return uri.slice(0, 4) === 'http';
|
|
74
|
+
}
|
|
87
75
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
var _iterator = _createForOfIteratorHelper(requests),
|
|
106
|
-
_step;
|
|
107
|
-
try {
|
|
108
|
-
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
109
|
-
var request = _step.value;
|
|
110
|
-
var _response = kb.any(request, this.ns.link('response'), null, meta);
|
|
111
|
-
if (_response != undefined) {
|
|
112
|
-
// ts
|
|
113
|
-
kb.add(_response, this.ns.link('outOfDate'), true, meta); // @@ Boolean is fine - fix types
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
} catch (err) {
|
|
117
|
-
_iterator.e(err);
|
|
118
|
-
} finally {
|
|
119
|
-
_iterator.f();
|
|
76
|
+
/** Remove from the store HTTP authorization metadata
|
|
77
|
+
* The editable function below relies on copies we have in the store
|
|
78
|
+
* of the results of previous HTTP transactions. However, when
|
|
79
|
+
* the user logs in, then that data misrepresents what would happen
|
|
80
|
+
* if the user tried again.
|
|
81
|
+
*/
|
|
82
|
+
flagAuthorizationMetadata(kb) {
|
|
83
|
+
if (!kb) {
|
|
84
|
+
kb = this.store;
|
|
85
|
+
}
|
|
86
|
+
const meta = kb.fetcher?.appNode;
|
|
87
|
+
const requests = kb.statementsMatching(undefined, this.ns.link('requestedURI'), undefined, meta).map(st => st.subject);
|
|
88
|
+
for (const request of requests) {
|
|
89
|
+
const response = kb.any(request, this.ns.link('response'), null, meta);
|
|
90
|
+
if (response != undefined) {
|
|
91
|
+
// ts
|
|
92
|
+
kb.add(response, this.ns.link('outOfDate'), true, meta); // @@ Boolean is fine - fix types
|
|
120
93
|
}
|
|
121
94
|
}
|
|
95
|
+
}
|
|
122
96
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
return
|
|
172
|
-
}
|
|
173
|
-
return checkEditable;
|
|
174
|
-
}()
|
|
175
|
-
/**
|
|
176
|
-
* Tests whether a file is editable.
|
|
177
|
-
* If the file has a specific annotation that it is machine written,
|
|
178
|
-
* for safety, it is editable (this doesn't actually check for write access)
|
|
179
|
-
* If the file has wac-allow and accept patch headers, those are respected.
|
|
180
|
-
* and local write access is determined by those headers.
|
|
181
|
-
* This synchronous version only looks at past HTTP requests, does not make new ones.
|
|
182
|
-
*
|
|
183
|
-
* @returns The method string SPARQL or DAV or
|
|
184
|
-
* LOCALFILE or false if known, undefined if not known.
|
|
185
|
-
*/
|
|
186
|
-
)
|
|
187
|
-
}, {
|
|
188
|
-
key: "editable",
|
|
189
|
-
value: function editable(uri, kb) {
|
|
190
|
-
var _kb$fetcher3;
|
|
191
|
-
if (!uri) {
|
|
192
|
-
return false; // Eg subject is bnode, no known doc to write to
|
|
193
|
-
}
|
|
194
|
-
if (!kb) {
|
|
195
|
-
kb = this.store;
|
|
196
|
-
}
|
|
197
|
-
uri = (0, _termValue.termValue)(uri);
|
|
198
|
-
if (!this.isHttpUri(uri)) {
|
|
199
|
-
if (kb.holds(kb.rdfFactory.namedNode(uri), kb.rdfFactory.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), kb.rdfFactory.namedNode('http://www.w3.org/2007/ont/link#MachineEditableDocument'))) {
|
|
200
|
-
return 'LOCALFILE';
|
|
201
|
-
}
|
|
97
|
+
/**
|
|
98
|
+
* Tests whether a file is editable.
|
|
99
|
+
* If the file has a specific annotation that it is machine written,
|
|
100
|
+
* for safety, it is editable (this doesn't actually check for write access)
|
|
101
|
+
* If the file has wac-allow and accept patch headers, those are respected.
|
|
102
|
+
* and local write access is determined by those headers.
|
|
103
|
+
* This async version not only looks at past HTTP requests, it also makes new ones if necessary.
|
|
104
|
+
*
|
|
105
|
+
* @returns The method string N3PATCH or SPARQL or DAV or
|
|
106
|
+
* LOCALFILE or false if known, undefined if not known.
|
|
107
|
+
*/
|
|
108
|
+
async checkEditable(uri, kb) {
|
|
109
|
+
if (!uri) {
|
|
110
|
+
return false; // Eg subject is bnode, no known doc to write to
|
|
111
|
+
}
|
|
112
|
+
if (!kb) {
|
|
113
|
+
kb = this.store;
|
|
114
|
+
}
|
|
115
|
+
const initial = this.editable(uri, kb);
|
|
116
|
+
if (initial !== undefined) {
|
|
117
|
+
return initial;
|
|
118
|
+
}
|
|
119
|
+
await kb.fetcher?.load(uri);
|
|
120
|
+
const final = this.editable(uri, kb);
|
|
121
|
+
// console.log(`Loaded ${uri} just to check editable, result: ${final}.`)
|
|
122
|
+
return final;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Tests whether a file is editable.
|
|
126
|
+
* If the file has a specific annotation that it is machine written,
|
|
127
|
+
* for safety, it is editable (this doesn't actually check for write access)
|
|
128
|
+
* If the file has wac-allow and accept patch headers, those are respected.
|
|
129
|
+
* and local write access is determined by those headers.
|
|
130
|
+
* This synchronous version only looks at past HTTP requests, does not make new ones.
|
|
131
|
+
*
|
|
132
|
+
* @returns The method string SPARQL or DAV or
|
|
133
|
+
* LOCALFILE or false if known, undefined if not known.
|
|
134
|
+
*/
|
|
135
|
+
editable(uri, kb) {
|
|
136
|
+
if (!uri) {
|
|
137
|
+
return false; // Eg subject is bnode, no known doc to write to
|
|
138
|
+
}
|
|
139
|
+
if (!kb) {
|
|
140
|
+
kb = this.store;
|
|
141
|
+
}
|
|
142
|
+
uri = (0, _termValue.termValue)(uri);
|
|
143
|
+
if (!this.isHttpUri(uri)) {
|
|
144
|
+
if (kb.holds(kb.rdfFactory.namedNode(uri), kb.rdfFactory.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), kb.rdfFactory.namedNode('http://www.w3.org/2007/ont/link#MachineEditableDocument'))) {
|
|
145
|
+
return 'LOCALFILE';
|
|
202
146
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
147
|
+
}
|
|
148
|
+
var request;
|
|
149
|
+
var definitive = false;
|
|
150
|
+
const meta = kb.fetcher?.appNode;
|
|
151
|
+
// const kb = s
|
|
207
152
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
153
|
+
// @ts-ignore passes a string to kb.each, which expects a term. Should this work?
|
|
154
|
+
var requests = kb.each(undefined, this.ns.link('requestedURI'), (0, _uri.docpart)(uri), meta);
|
|
155
|
+
var method;
|
|
156
|
+
for (var r = 0; r < requests.length; r++) {
|
|
157
|
+
request = requests[r];
|
|
158
|
+
if (request !== undefined) {
|
|
159
|
+
const response = kb.any(request, this.ns.link('response'), null, meta);
|
|
160
|
+
if (response !== undefined) {
|
|
161
|
+
// ts
|
|
217
162
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
var lr = bit.split('=');
|
|
228
|
-
if (lr[0].includes('user') && !lr[1].includes('write') && !lr[1].includes('append')) {
|
|
229
|
-
// console.log(' editable? excluded by WAC-Allow: ', wacAllow)
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
} catch (err) {
|
|
234
|
-
_iterator2.e(err);
|
|
235
|
-
} finally {
|
|
236
|
-
_iterator2.f();
|
|
163
|
+
const outOfDate = kb.anyJS(response, this.ns.link('outOfDate'), null, meta);
|
|
164
|
+
if (outOfDate) continue;
|
|
165
|
+
var wacAllow = kb.anyValue(response, this.ns.httph('wac-allow'));
|
|
166
|
+
if (wacAllow) {
|
|
167
|
+
for (var bit of wacAllow.split(',')) {
|
|
168
|
+
var lr = bit.split('=');
|
|
169
|
+
if (lr[0].includes('user') && !lr[1].includes('write') && !lr[1].includes('append')) {
|
|
170
|
+
// console.log(' editable? excluded by WAC-Allow: ', wacAllow)
|
|
171
|
+
return false;
|
|
237
172
|
}
|
|
238
173
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
174
|
+
}
|
|
175
|
+
var acceptPatch = kb.each(response, this.ns.httph('accept-patch'));
|
|
176
|
+
if (acceptPatch.length) {
|
|
177
|
+
for (let i = 0; i < acceptPatch.length; i++) {
|
|
178
|
+
method = acceptPatch[i].value.trim();
|
|
179
|
+
if (method.indexOf('text/n3') >= 0) return 'N3PATCH';
|
|
180
|
+
if (method.indexOf('application/sparql-update') >= 0) return 'SPARQL';
|
|
181
|
+
if (method.indexOf('application/sparql-update-single-match') >= 0) return 'SPARQL';
|
|
247
182
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
183
|
+
}
|
|
184
|
+
var authorVia = kb.each(response, this.ns.httph('ms-author-via'));
|
|
185
|
+
if (authorVia.length) {
|
|
186
|
+
for (let i = 0; i < authorVia.length; i++) {
|
|
187
|
+
method = authorVia[i].value.trim();
|
|
188
|
+
if (method.indexOf('SPARQL') >= 0) {
|
|
189
|
+
return 'SPARQL';
|
|
190
|
+
}
|
|
191
|
+
if (method.indexOf('DAV') >= 0) {
|
|
192
|
+
return 'DAV';
|
|
258
193
|
}
|
|
259
194
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
195
|
+
}
|
|
196
|
+
if (!this.isHttpUri(uri)) {
|
|
197
|
+
if (!wacAllow) return false;else return 'LOCALFILE';
|
|
198
|
+
}
|
|
199
|
+
var status = kb.each(response, this.ns.http('status'));
|
|
200
|
+
if (status.length) {
|
|
201
|
+
for (let i = 0; i < status.length; i++) {
|
|
202
|
+
// @ts-ignore since statuses should be TFTerms, this should always be false
|
|
203
|
+
if (status[i] === 200 || status[i] === 404) {
|
|
204
|
+
definitive = true;
|
|
205
|
+
// return false // A definitive answer
|
|
271
206
|
}
|
|
272
207
|
}
|
|
273
|
-
} else {
|
|
274
|
-
// console.log('UpdateManager.editable: No response for ' + uri + '\n')
|
|
275
208
|
}
|
|
209
|
+
} else {
|
|
210
|
+
// console.log('UpdateManager.editable: No response for ' + uri + '\n')
|
|
276
211
|
}
|
|
277
212
|
}
|
|
278
|
-
if (requests.length === 0) {
|
|
279
|
-
// console.log('UpdateManager.editable: No request for ' + uri + '\n')
|
|
280
|
-
} else {
|
|
281
|
-
if (definitive) {
|
|
282
|
-
return false; // We have got a request and it did NOT say editable => not editable
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
// console.log('UpdateManager.editable: inconclusive for ' + uri + '\n')
|
|
286
|
-
return undefined; // We don't know (yet) as we haven't had a response (yet)
|
|
287
|
-
}
|
|
288
|
-
}, {
|
|
289
|
-
key: "anonymize",
|
|
290
|
-
value: function anonymize(obj) {
|
|
291
|
-
var anonymized = obj.toNT().substr(0, 2) === '_:' && this.mentioned(obj) ? '?' + obj.toNT().substr(2) : obj.toNT();
|
|
292
|
-
return anonymized;
|
|
293
|
-
}
|
|
294
|
-
}, {
|
|
295
|
-
key: "anonymizeNT",
|
|
296
|
-
value: function anonymizeNT(stmt) {
|
|
297
|
-
return this.anonymize(stmt.subject) + ' ' + this.anonymize(stmt.predicate) + ' ' + this.anonymize(stmt.object) + ' .';
|
|
298
213
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
214
|
+
if (requests.length === 0) {
|
|
215
|
+
// console.log('UpdateManager.editable: No request for ' + uri + '\n')
|
|
216
|
+
} else {
|
|
217
|
+
if (definitive) {
|
|
218
|
+
return false; // We have got a request and it did NOT say editable => not editable
|
|
219
|
+
}
|
|
303
220
|
}
|
|
221
|
+
// console.log('UpdateManager.editable: inconclusive for ' + uri + '\n')
|
|
222
|
+
return undefined; // We don't know (yet) as we haven't had a response (yet)
|
|
223
|
+
}
|
|
224
|
+
anonymize(obj) {
|
|
225
|
+
let anonymized = obj.toNT().substr(0, 2) === '_:' && this.mentioned(obj) ? '?' + obj.toNT().substr(2) : obj.toNT();
|
|
226
|
+
return anonymized;
|
|
227
|
+
}
|
|
228
|
+
anonymizeNT(stmt) {
|
|
229
|
+
return this.anonymize(stmt.subject) + ' ' + this.anonymize(stmt.predicate) + ' ' + this.anonymize(stmt.object) + ' .';
|
|
230
|
+
}
|
|
231
|
+
nTriples(stmt) {
|
|
232
|
+
return `${stmt.subject.toNT()} ${stmt.predicate.toNT()} ${stmt.object.toNT()} .`;
|
|
233
|
+
}
|
|
304
234
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
});
|
|
315
|
-
}
|
|
235
|
+
/**
|
|
236
|
+
* Returns a list of all bnodes occurring in a statement
|
|
237
|
+
* @private
|
|
238
|
+
*/
|
|
239
|
+
statementBnodes(st) {
|
|
240
|
+
return [st.subject, st.predicate, st.object].filter(function (x) {
|
|
241
|
+
return (0, _terms.isBlankNode)(x);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
316
244
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
if (j === 0 || !bnodes[j].equals(bnodes[j - 1])) {
|
|
332
|
-
bnodes2.push(bnodes[j]);
|
|
333
|
-
}
|
|
245
|
+
/**
|
|
246
|
+
* Returns a list of all bnodes occurring in a list of statements
|
|
247
|
+
* @private
|
|
248
|
+
*/
|
|
249
|
+
statementArrayBnodes(sts) {
|
|
250
|
+
var bnodes = [];
|
|
251
|
+
for (let i = 0; i < sts.length; i++) {
|
|
252
|
+
bnodes = bnodes.concat(this.statementBnodes(sts[i]));
|
|
253
|
+
}
|
|
254
|
+
bnodes.sort(); // in place sort - result may have duplicates
|
|
255
|
+
var bnodes2 = [];
|
|
256
|
+
for (let j = 0; j < bnodes.length; j++) {
|
|
257
|
+
if (j === 0 || !bnodes[j].equals(bnodes[j - 1])) {
|
|
258
|
+
bnodes2.push(bnodes[j]);
|
|
334
259
|
}
|
|
335
|
-
return bnodes2;
|
|
336
260
|
}
|
|
261
|
+
return bnodes2;
|
|
262
|
+
}
|
|
337
263
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
for (var i = 0; i < a.length; i++) {
|
|
348
|
-
this.ifps[a[i].value] = true;
|
|
349
|
-
}
|
|
350
|
-
this.fps = {};
|
|
351
|
-
a = this.store.each(undefined, this.ns.rdf('type'), this.ns.owl('FunctionalProperty'));
|
|
352
|
-
for (var _i3 = 0; _i3 < a.length; _i3++) {
|
|
353
|
-
this.fps[a[_i3].value] = true;
|
|
354
|
-
}
|
|
264
|
+
/**
|
|
265
|
+
* Makes a cached list of [Inverse-]Functional properties
|
|
266
|
+
* @private
|
|
267
|
+
*/
|
|
268
|
+
cacheIfps() {
|
|
269
|
+
this.ifps = {};
|
|
270
|
+
var a = this.store.each(undefined, this.ns.rdf('type'), this.ns.owl('InverseFunctionalProperty'));
|
|
271
|
+
for (let i = 0; i < a.length; i++) {
|
|
272
|
+
this.ifps[a[i].value] = true;
|
|
355
273
|
}
|
|
274
|
+
this.fps = {};
|
|
275
|
+
a = this.store.each(undefined, this.ns.rdf('type'), this.ns.owl('FunctionalProperty'));
|
|
276
|
+
for (let i = 0; i < a.length; i++) {
|
|
277
|
+
this.fps[a[i].value] = true;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
356
280
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
if (
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if (
|
|
377
|
-
res
|
|
378
|
-
if (res) {
|
|
379
|
-
return res.concat([sts[i]]);
|
|
380
|
-
}
|
|
281
|
+
/**
|
|
282
|
+
* Returns a context to bind a given node, up to a given depth
|
|
283
|
+
* @private
|
|
284
|
+
*/
|
|
285
|
+
bnodeContext2(x, source, depth) {
|
|
286
|
+
// Return a list of statements which indirectly identify a node
|
|
287
|
+
// Depth > 1 if try further indirection.
|
|
288
|
+
// Return array of statements (possibly empty), or null if failure
|
|
289
|
+
var sts = this.store.statementsMatching(undefined, undefined, x, source); // incoming links
|
|
290
|
+
var y;
|
|
291
|
+
var res;
|
|
292
|
+
for (let i = 0; i < sts.length; i++) {
|
|
293
|
+
if (this.fps[sts[i].predicate.value]) {
|
|
294
|
+
y = sts[i].subject;
|
|
295
|
+
if (!y.isBlank) {
|
|
296
|
+
return [sts[i]];
|
|
297
|
+
}
|
|
298
|
+
if (depth) {
|
|
299
|
+
res = this.bnodeContext2(y, source, depth - 1);
|
|
300
|
+
if (res) {
|
|
301
|
+
return res.concat([sts[i]]);
|
|
381
302
|
}
|
|
382
303
|
}
|
|
383
304
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
305
|
+
}
|
|
306
|
+
// outgoing links
|
|
307
|
+
sts = this.store.statementsMatching(x, undefined, undefined, source);
|
|
308
|
+
for (let i = 0; i < sts.length; i++) {
|
|
309
|
+
if (this.ifps[sts[i].predicate.value]) {
|
|
310
|
+
y = sts[i].object;
|
|
311
|
+
if (!y.isBlank) {
|
|
312
|
+
return [sts[i]];
|
|
313
|
+
}
|
|
314
|
+
if (depth) {
|
|
315
|
+
res = this.bnodeContext2(y, source, depth - 1);
|
|
316
|
+
if (res) {
|
|
317
|
+
return res.concat([sts[i]]);
|
|
397
318
|
}
|
|
398
319
|
}
|
|
399
320
|
}
|
|
400
|
-
return null; // Failure
|
|
401
321
|
}
|
|
322
|
+
return null; // Failure
|
|
323
|
+
}
|
|
402
324
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
//
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
var con = this.bnodeContext2(x, source, depth);
|
|
415
|
-
if (con !== null) return con;
|
|
416
|
-
}
|
|
417
|
-
// If we can't guarantee unique with logic just send all info about node
|
|
418
|
-
return this.store.connectedStatements(x, source); // was:
|
|
419
|
-
// throw new Error('Unable to uniquely identify bnode: ' + x.toNT())
|
|
325
|
+
/**
|
|
326
|
+
* Returns the smallest context to bind a given single bnode
|
|
327
|
+
* @private
|
|
328
|
+
*/
|
|
329
|
+
bnodeContext1(x, source) {
|
|
330
|
+
// Return a list of statements which indirectly identify a node
|
|
331
|
+
// Breadth-first
|
|
332
|
+
for (var depth = 0; depth < 3; depth++) {
|
|
333
|
+
// Try simple first
|
|
334
|
+
var con = this.bnodeContext2(x, source, depth);
|
|
335
|
+
if (con !== null) return con;
|
|
420
336
|
}
|
|
337
|
+
// If we can't guarantee unique with logic just send all info about node
|
|
338
|
+
return this.store.connectedStatements(x, source); // was:
|
|
339
|
+
// throw new Error('Unable to uniquely identify bnode: ' + x.toNT())
|
|
340
|
+
}
|
|
421
341
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
this.store.statementsMatching(null, x).length !== 0 || this.store.statementsMatching(null, null, x).length !== 0;
|
|
431
|
-
}
|
|
342
|
+
/**
|
|
343
|
+
* @private
|
|
344
|
+
*/
|
|
345
|
+
mentioned(x) {
|
|
346
|
+
return this.store.statementsMatching(x, null, null, null).length !== 0 ||
|
|
347
|
+
// Don't pin fresh bnodes
|
|
348
|
+
this.store.statementsMatching(null, x).length !== 0 || this.store.statementsMatching(null, null, x).length !== 0;
|
|
349
|
+
}
|
|
432
350
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
this
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
if (!this.mentioned(bnode)) continue;
|
|
446
|
-
context = context.concat(this.bnodeContext1(bnode, doc));
|
|
447
|
-
}
|
|
351
|
+
/**
|
|
352
|
+
* @private
|
|
353
|
+
*/
|
|
354
|
+
bnodeContext(bnodes, doc) {
|
|
355
|
+
var context = [];
|
|
356
|
+
if (bnodes.length) {
|
|
357
|
+
this.cacheIfps();
|
|
358
|
+
for (let i = 0; i < bnodes.length; i++) {
|
|
359
|
+
// Does this occur in old graph?
|
|
360
|
+
var bnode = bnodes[i];
|
|
361
|
+
if (!this.mentioned(bnode)) continue;
|
|
362
|
+
context = context.concat(this.bnodeContext1(bnode, doc));
|
|
448
363
|
}
|
|
449
|
-
return context;
|
|
450
364
|
}
|
|
365
|
+
return context;
|
|
366
|
+
}
|
|
451
367
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
return this.bnodeContext(bnodes, st.graph);
|
|
461
|
-
}
|
|
368
|
+
/**
|
|
369
|
+
* Returns the best context for a single statement
|
|
370
|
+
* @private
|
|
371
|
+
*/
|
|
372
|
+
statementContext(st) {
|
|
373
|
+
var bnodes = this.statementBnodes(st);
|
|
374
|
+
return this.bnodeContext(bnodes, st.graph);
|
|
375
|
+
}
|
|
462
376
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
}).join('\n') + ' }\n';
|
|
473
|
-
}
|
|
377
|
+
/**
|
|
378
|
+
* @private
|
|
379
|
+
*/
|
|
380
|
+
contextWhere(context) {
|
|
381
|
+
var updater = this;
|
|
382
|
+
return !context || context.length === 0 ? '' : 'WHERE { ' + context.map(function (x) {
|
|
383
|
+
return updater.anonymizeNT(x);
|
|
384
|
+
}).join('\n') + ' }\n';
|
|
385
|
+
}
|
|
474
386
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
throw new Error('No URI given for remote editing operation: ' + query);
|
|
486
|
-
}
|
|
487
|
-
// console.log('UpdateManager: sending update to <' + uri + '>')
|
|
387
|
+
/**
|
|
388
|
+
* @private
|
|
389
|
+
*/
|
|
390
|
+
fire(uri, query, callbackFunction) {
|
|
391
|
+
let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
392
|
+
return Promise.resolve().then(() => {
|
|
393
|
+
if (!uri) {
|
|
394
|
+
throw new Error('No URI given for remote editing operation: ' + query);
|
|
395
|
+
}
|
|
396
|
+
// console.log('UpdateManager: sending update to <' + uri + '>')
|
|
488
397
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
398
|
+
options.noMeta = true;
|
|
399
|
+
options.contentType = options.contentType || 'application/sparql-update';
|
|
400
|
+
options.body = query;
|
|
401
|
+
return this.store.fetcher.webOperation('PATCH', uri, options);
|
|
402
|
+
}).then(response => {
|
|
403
|
+
if (!response.ok) {
|
|
404
|
+
let message = 'UpdateManager: update failed for <' + uri + '> status=' + response.status + ', ' + response.statusText + '\n for query: ' + query;
|
|
405
|
+
// console.log(message)
|
|
406
|
+
throw new Error(message);
|
|
407
|
+
}
|
|
499
408
|
|
|
500
|
-
|
|
409
|
+
// console.log('UpdateManager: update Ok for <' + uri + '>')
|
|
501
410
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
411
|
+
callbackFunction(uri, response.ok, response.responseText, response);
|
|
412
|
+
}).catch(err => {
|
|
413
|
+
callbackFunction(uri, false, err.message, err);
|
|
414
|
+
});
|
|
415
|
+
}
|
|
507
416
|
|
|
508
|
-
|
|
417
|
+
// ARE THESE THREE FUNCTIONS USED? DEPRECATE?
|
|
509
418
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
if (statement && !statement.graph) {
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
var updater = this;
|
|
523
|
-
var context = this.statementContext(statement);
|
|
524
|
-
return {
|
|
525
|
-
statement: statement ? [statement.subject, statement.predicate, statement.object, statement.graph] : undefined,
|
|
526
|
-
statementNT: statement ? this.anonymizeNT(statement) : undefined,
|
|
527
|
-
where: updater.contextWhere(context),
|
|
528
|
-
set_object: function set_object(obj, callbackFunction) {
|
|
529
|
-
var query = this.where;
|
|
530
|
-
query += 'DELETE DATA { ' + this.statementNT + ' } ;\n';
|
|
531
|
-
query += 'INSERT DATA { ' +
|
|
532
|
-
// @ts-ignore `this` might refer to the wrong scope. Does this work?
|
|
533
|
-
this.anonymize(this.statement[0]) + ' ' +
|
|
534
|
-
// @ts-ignore
|
|
535
|
-
this.anonymize(this.statement[1]) + ' ' +
|
|
536
|
-
// @ts-ignore
|
|
537
|
-
this.anonymize(obj) + ' ' + ' . }\n';
|
|
538
|
-
updater.fire(this.statement[3].value, query, callbackFunction);
|
|
539
|
-
}
|
|
540
|
-
};
|
|
419
|
+
/** return a statemnet updating function
|
|
420
|
+
*
|
|
421
|
+
* This does NOT update the statement.
|
|
422
|
+
* It returns an object which includes
|
|
423
|
+
* function which can be used to change the object of the statement.
|
|
424
|
+
*/
|
|
425
|
+
update_statement(statement) {
|
|
426
|
+
if (statement && !statement.graph) {
|
|
427
|
+
return;
|
|
541
428
|
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
query += '
|
|
551
|
-
|
|
552
|
-
|
|
429
|
+
var updater = this;
|
|
430
|
+
var context = this.statementContext(statement);
|
|
431
|
+
return {
|
|
432
|
+
statement: statement ? [statement.subject, statement.predicate, statement.object, statement.graph] : undefined,
|
|
433
|
+
statementNT: statement ? this.anonymizeNT(statement) : undefined,
|
|
434
|
+
where: updater.contextWhere(context),
|
|
435
|
+
set_object: function (obj, callbackFunction) {
|
|
436
|
+
var query = this.where;
|
|
437
|
+
query += 'DELETE DATA { ' + this.statementNT + ' } ;\n';
|
|
438
|
+
query += 'INSERT DATA { ' +
|
|
439
|
+
// @ts-ignore `this` might refer to the wrong scope. Does this work?
|
|
440
|
+
this.anonymize(this.statement[0]) + ' ' +
|
|
441
|
+
// @ts-ignore
|
|
442
|
+
this.anonymize(this.statement[1]) + ' ' +
|
|
443
|
+
// @ts-ignore
|
|
444
|
+
this.anonymize(obj) + ' ' + ' . }\n';
|
|
445
|
+
updater.fire(this.statement[3].value, query, callbackFunction);
|
|
553
446
|
}
|
|
554
|
-
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
insert_statement(st, callbackFunction) {
|
|
450
|
+
var st0 = st instanceof Array ? st[0] : st;
|
|
451
|
+
var query = this.contextWhere(this.statementContext(st0));
|
|
452
|
+
if (st instanceof Array) {
|
|
453
|
+
var stText = '';
|
|
454
|
+
for (let i = 0; i < st.length; i++) stText += st[i] + '\n';
|
|
455
|
+
query += 'INSERT DATA { ' + stText + ' }\n';
|
|
456
|
+
} else {
|
|
457
|
+
query += 'INSERT DATA { ' + this.anonymize(st.subject) + ' ' + this.anonymize(st.predicate) + ' ' + this.anonymize(st.object) + ' ' + ' . }\n';
|
|
555
458
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
}
|
|
568
|
-
this.fire(st0.graph.value, query, callbackFunction);
|
|
459
|
+
this.fire(st0.graph.value, query, callbackFunction);
|
|
460
|
+
}
|
|
461
|
+
delete_statement(st, callbackFunction) {
|
|
462
|
+
var st0 = st instanceof Array ? st[0] : st;
|
|
463
|
+
var query = this.contextWhere(this.statementContext(st0));
|
|
464
|
+
if (st instanceof Array) {
|
|
465
|
+
var stText = '';
|
|
466
|
+
for (let i = 0; i < st.length; i++) stText += st[i] + '\n';
|
|
467
|
+
query += 'DELETE DATA { ' + stText + ' }\n';
|
|
468
|
+
} else {
|
|
469
|
+
query += 'DELETE DATA { ' + this.anonymize(st.subject) + ' ' + this.anonymize(st.predicate) + ' ' + this.anonymize(st.object) + ' ' + ' . }\n';
|
|
569
470
|
}
|
|
471
|
+
this.fire(st0.graph.value, query, callbackFunction);
|
|
472
|
+
}
|
|
570
473
|
|
|
571
|
-
|
|
474
|
+
/// //////////////////////
|
|
572
475
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
// Kludge compare
|
|
592
|
-
throw new Error("Can't wait for > 1 different downstream actions");
|
|
593
|
-
}
|
|
594
|
-
} else {
|
|
595
|
-
control.downstreamAction = action;
|
|
476
|
+
/**
|
|
477
|
+
* Requests a now or future action to refresh changes coming downstream
|
|
478
|
+
* This is designed to allow the system to re-request the server version,
|
|
479
|
+
* when a websocket has pinged to say there are changes.
|
|
480
|
+
* If the websocket, by contrast, has sent a patch, then this may not be necessary.
|
|
481
|
+
*
|
|
482
|
+
* @param doc
|
|
483
|
+
* @param action
|
|
484
|
+
*/
|
|
485
|
+
requestDownstreamAction(doc, action) {
|
|
486
|
+
var control = this.patchControlFor(doc);
|
|
487
|
+
if (!control.pendingUpstream) {
|
|
488
|
+
action(doc);
|
|
489
|
+
} else {
|
|
490
|
+
if (control.downstreamAction) {
|
|
491
|
+
if ('' + control.downstreamAction !== '' + action) {
|
|
492
|
+
// Kludge compare
|
|
493
|
+
throw new Error("Can't wait for > 1 different downstream actions");
|
|
596
494
|
}
|
|
495
|
+
} else {
|
|
496
|
+
control.downstreamAction = action;
|
|
597
497
|
}
|
|
598
498
|
}
|
|
499
|
+
}
|
|
599
500
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
501
|
+
/**
|
|
502
|
+
* We want to start counting websocket notifications
|
|
503
|
+
* to distinguish the ones from others from our own.
|
|
504
|
+
*/
|
|
505
|
+
clearUpstreamCount(doc) {
|
|
506
|
+
var control = this.patchControlFor(doc);
|
|
507
|
+
control.upstreamCount = 0;
|
|
508
|
+
}
|
|
509
|
+
getUpdatesVia(doc) {
|
|
510
|
+
var linkHeaders = this.store.fetcher.getHeader(doc, 'updates-via');
|
|
511
|
+
if (!linkHeaders || !linkHeaders.length) return null;
|
|
512
|
+
return linkHeaders[0].trim();
|
|
513
|
+
}
|
|
514
|
+
addDownstreamChangeListener(doc, listener) {
|
|
515
|
+
var control = this.patchControlFor(doc);
|
|
516
|
+
if (!control.downstreamChangeListeners) {
|
|
517
|
+
control.downstreamChangeListeners = [];
|
|
616
518
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
519
|
+
control.downstreamChangeListeners.push(listener);
|
|
520
|
+
this.setRefreshHandler(doc, doc => {
|
|
521
|
+
this.reloadAndSync(doc);
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
reloadAndSync(doc) {
|
|
525
|
+
var control = this.patchControlFor(doc);
|
|
526
|
+
var updater = this;
|
|
527
|
+
if (control.reloading) {
|
|
528
|
+
// console.log(' Already reloading - note this load may be out of date')
|
|
529
|
+
control.outOfDate = true;
|
|
530
|
+
return; // once only needed @@ Not true, has changed again
|
|
629
531
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
control.reloading = true;
|
|
641
|
-
var retryTimeout = 1000; // ms
|
|
642
|
-
var tryReload = function tryReload() {
|
|
643
|
-
// console.log('try reload - timeout = ' + retryTimeout)
|
|
644
|
-
updater.reload(updater.store, doc, function (ok, message, response) {
|
|
645
|
-
if (ok) {
|
|
646
|
-
if (control.downstreamChangeListeners) {
|
|
647
|
-
for (var i = 0; i < control.downstreamChangeListeners.length; i++) {
|
|
648
|
-
// console.log(' Calling downstream listener ' + i)
|
|
649
|
-
control.downstreamChangeListeners[i]();
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
control.reloading = false;
|
|
653
|
-
if (control.outOfDate) {
|
|
654
|
-
// console.log(' Extra reload because of extra update.')
|
|
655
|
-
control.outOfDate = false;
|
|
656
|
-
tryReload();
|
|
532
|
+
control.reloading = true;
|
|
533
|
+
var retryTimeout = 1000; // ms
|
|
534
|
+
var tryReload = function () {
|
|
535
|
+
// console.log('try reload - timeout = ' + retryTimeout)
|
|
536
|
+
updater.reload(updater.store, doc, function (ok, message, response) {
|
|
537
|
+
if (ok) {
|
|
538
|
+
if (control.downstreamChangeListeners) {
|
|
539
|
+
for (let i = 0; i < control.downstreamChangeListeners.length; i++) {
|
|
540
|
+
// console.log(' Calling downstream listener ' + i)
|
|
541
|
+
control.downstreamChangeListeners[i]();
|
|
657
542
|
}
|
|
543
|
+
}
|
|
544
|
+
control.reloading = false;
|
|
545
|
+
if (control.outOfDate) {
|
|
546
|
+
// console.log(' Extra reload because of extra update.')
|
|
547
|
+
control.outOfDate = false;
|
|
548
|
+
tryReload();
|
|
549
|
+
}
|
|
550
|
+
} else {
|
|
551
|
+
control.reloading = false;
|
|
552
|
+
if (response && response.status === 0) {
|
|
553
|
+
// console.log('Network error refreshing the data. Retrying in ' +
|
|
554
|
+
// retryTimeout / 1000)
|
|
555
|
+
control.reloading = true;
|
|
556
|
+
retryTimeout = retryTimeout * 2;
|
|
557
|
+
setTimeout(tryReload, retryTimeout);
|
|
658
558
|
} else {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
// console.log('Network error refreshing the data. Retrying in ' +
|
|
662
|
-
// retryTimeout / 1000)
|
|
663
|
-
control.reloading = true;
|
|
664
|
-
retryTimeout = retryTimeout * 2;
|
|
665
|
-
setTimeout(tryReload, retryTimeout);
|
|
666
|
-
} else {
|
|
667
|
-
// console.log('Error ' + (response as Response).status + 'refreshing the data:' +
|
|
668
|
-
// message + '. Stopped' + doc)
|
|
669
|
-
}
|
|
559
|
+
// console.log('Error ' + (response as Response).status + 'refreshing the data:' +
|
|
560
|
+
// message + '. Stopped' + doc)
|
|
670
561
|
}
|
|
671
|
-
}
|
|
672
|
-
};
|
|
673
|
-
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
};
|
|
565
|
+
tryReload();
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Sets up websocket to listen on
|
|
570
|
+
*
|
|
571
|
+
* There is coordination between upstream changes and downstream ones
|
|
572
|
+
* so that a reload is not done in the middle of an upstream patch.
|
|
573
|
+
* If you use this API then you get called when a change happens, and you
|
|
574
|
+
* have to reload the file yourself, and then refresh the UI.
|
|
575
|
+
* Alternative is addDownstreamChangeListener(), where you do not
|
|
576
|
+
* have to do the reload yourself. Do mot mix them.
|
|
577
|
+
*
|
|
578
|
+
* kb contains the HTTP metadata from previous operations
|
|
579
|
+
*
|
|
580
|
+
* @param doc
|
|
581
|
+
* @param handler
|
|
582
|
+
*
|
|
583
|
+
* @returns {boolean}
|
|
584
|
+
*/
|
|
585
|
+
setRefreshHandler(doc, handler) {
|
|
586
|
+
let wssURI = this.getUpdatesVia(doc); // relative
|
|
587
|
+
// var kb = this.store
|
|
588
|
+
var theHandler = handler;
|
|
589
|
+
var self = this;
|
|
590
|
+
var updater = this;
|
|
591
|
+
var retryTimeout = 1500; // *2 will be 3 Seconds, 6, 12, etc
|
|
592
|
+
var retries = 0;
|
|
593
|
+
if (!wssURI) {
|
|
594
|
+
// console.log('Server does not support live updates through Updates-Via :-(')
|
|
595
|
+
return false;
|
|
674
596
|
}
|
|
597
|
+
wssURI = (0, _uri.join)(wssURI, doc.value);
|
|
598
|
+
const validWssURI = wssURI.replace(/^http:/, 'ws:').replace(/^https:/, 'wss:');
|
|
599
|
+
// console.log('Web socket URI ' + wssURI)
|
|
675
600
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
* kb contains the HTTP metadata from previous operations
|
|
687
|
-
*
|
|
688
|
-
* @param doc
|
|
689
|
-
* @param handler
|
|
690
|
-
*
|
|
691
|
-
* @returns {boolean}
|
|
692
|
-
*/
|
|
693
|
-
}, {
|
|
694
|
-
key: "setRefreshHandler",
|
|
695
|
-
value: function setRefreshHandler(doc, handler) {
|
|
696
|
-
var wssURI = this.getUpdatesVia(doc); // relative
|
|
697
|
-
// var kb = this.store
|
|
698
|
-
var theHandler = handler;
|
|
699
|
-
var self = this;
|
|
700
|
-
var updater = this;
|
|
701
|
-
var retryTimeout = 1500; // *2 will be 3 Seconds, 6, 12, etc
|
|
702
|
-
var retries = 0;
|
|
703
|
-
if (!wssURI) {
|
|
704
|
-
// console.log('Server does not support live updates through Updates-Via :-(')
|
|
705
|
-
return false;
|
|
601
|
+
var openWebsocket = function () {
|
|
602
|
+
// From https://github.com/solid/solid-spec#live-updates
|
|
603
|
+
var socket;
|
|
604
|
+
if (typeof WebSocket !== 'undefined') {
|
|
605
|
+
socket = new WebSocket(validWssURI);
|
|
606
|
+
} else if (typeof window !== 'undefined' && window.WebSocket) {
|
|
607
|
+
socket = window.WebSocket(validWssURI);
|
|
608
|
+
} else {
|
|
609
|
+
// console.log('Live update disabled, as WebSocket not supported by platform :-(')
|
|
610
|
+
return;
|
|
706
611
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
if (typeof WebSocket !== 'undefined') {
|
|
715
|
-
socket = new WebSocket(validWssURI);
|
|
716
|
-
} else if (typeof window !== 'undefined' && window.WebSocket) {
|
|
717
|
-
socket = window.WebSocket(validWssURI);
|
|
718
|
-
} else {
|
|
719
|
-
// console.log('Live update disabled, as WebSocket not supported by platform :-(')
|
|
720
|
-
return;
|
|
612
|
+
socket.onopen = function () {
|
|
613
|
+
// console.log(' websocket open')
|
|
614
|
+
retryTimeout = 1500; // reset timeout to fast on success
|
|
615
|
+
this.send('sub ' + doc.value);
|
|
616
|
+
if (retries) {
|
|
617
|
+
// console.log('Web socket has been down, better check for any news.')
|
|
618
|
+
updater.requestDownstreamAction(doc, theHandler);
|
|
721
619
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
updater.requestDownstreamAction(doc, theHandler);
|
|
729
|
-
}
|
|
730
|
-
};
|
|
731
|
-
var control = self.patchControlFor(doc);
|
|
732
|
-
control.upstreamCount = 0;
|
|
733
|
-
socket.onerror = function onerror(err) {
|
|
734
|
-
// console.log('Error on Websocket:', err)
|
|
735
|
-
};
|
|
620
|
+
};
|
|
621
|
+
var control = self.patchControlFor(doc);
|
|
622
|
+
control.upstreamCount = 0;
|
|
623
|
+
socket.onerror = function onerror(err) {
|
|
624
|
+
// console.log('Error on Websocket:', err)
|
|
625
|
+
};
|
|
736
626
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
}
|
|
627
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
|
628
|
+
//
|
|
629
|
+
// 1000 CLOSE_NORMAL Normal closure; the connection successfully completed whatever purpose for which it was created.
|
|
630
|
+
// 1001 CLOSE_GOING_AWAY The endpoint is going away, either
|
|
631
|
+
// because of a server failure or because the browser is navigating away from the page that opened the connection.
|
|
632
|
+
// 1002 CLOSE_PROTOCOL_ERROR The endpoint is terminating the connection due to a protocol error.
|
|
633
|
+
// 1003 CLOSE_UNSUPPORTED The connection is being terminated because the endpoint
|
|
634
|
+
// received data of a type it cannot accept (for example, a text-only endpoint received binary data).
|
|
635
|
+
// 1004 Reserved. A meaning might be defined in the future.
|
|
636
|
+
// 1005 CLOSE_NO_STATUS Reserved. Indicates that no status code was provided even though one was expected.
|
|
637
|
+
// 1006 CLOSE_ABNORMAL Reserved. Used to indicate that a connection was closed abnormally (
|
|
638
|
+
//
|
|
639
|
+
//
|
|
640
|
+
socket.onclose = function (event) {
|
|
641
|
+
// console.log('*** Websocket closed with code ' + event.code +
|
|
642
|
+
// ", reason '" + event.reason + "' clean = " + event.wasClean)
|
|
643
|
+
retryTimeout *= 2;
|
|
644
|
+
retries += 1;
|
|
645
|
+
// console.log('Retrying in ' + retryTimeout + 'ms') // (ask user?)
|
|
646
|
+
setTimeout(function () {
|
|
647
|
+
// console.log('Trying websocket again')
|
|
648
|
+
openWebsocket();
|
|
649
|
+
}, retryTimeout);
|
|
650
|
+
};
|
|
651
|
+
socket.onmessage = function (msg) {
|
|
652
|
+
if (msg.data && msg.data.slice(0, 3) === 'pub') {
|
|
653
|
+
if ('upstreamCount' in control) {
|
|
654
|
+
control.upstreamCount -= 1;
|
|
655
|
+
if (control.upstreamCount >= 0) {
|
|
656
|
+
// console.log('just an echo: ' + control.upstreamCount)
|
|
657
|
+
return; // Just an echo
|
|
769
658
|
}
|
|
770
|
-
// console.log('Assume a real downstream change: ' + control.upstreamCount + ' -> 0')
|
|
771
|
-
control.upstreamCount = 0;
|
|
772
|
-
self.requestDownstreamAction(doc, theHandler);
|
|
773
659
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
660
|
+
// console.log('Assume a real downstream change: ' + control.upstreamCount + ' -> 0')
|
|
661
|
+
control.upstreamCount = 0;
|
|
662
|
+
self.requestDownstreamAction(doc, theHandler);
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
}; // openWebsocket
|
|
666
|
+
openWebsocket();
|
|
667
|
+
return true;
|
|
668
|
+
}
|
|
779
669
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
return uniqueDoc.equals(doc);
|
|
801
|
-
})) uniqueDocs.push(doc);
|
|
802
|
-
});
|
|
803
|
-
var updates = uniqueDocs.map(function (doc) {
|
|
804
|
-
return thisUpdater.update(deletions.filter(function (st) {
|
|
805
|
-
return st.why.equals(doc);
|
|
806
|
-
}), insertions.filter(function (st) {
|
|
807
|
-
return st.why.equals(doc);
|
|
808
|
-
}));
|
|
809
|
-
});
|
|
810
|
-
if (updates.length > 1) {
|
|
811
|
-
// console.log(`@@ updateMany to ${updates.length}: ${uniqueDocs}`)
|
|
812
|
-
}
|
|
813
|
-
return Promise.all(updates);
|
|
670
|
+
/**
|
|
671
|
+
* This high-level function updates the local store iff the web is changed successfully.
|
|
672
|
+
* Deletions, insertions may be undefined or single statements or lists or formulae (may contain bnodes which can be indirectly identified by a where clause).
|
|
673
|
+
* The `why` property of each statement must be the give the web document to be updated.
|
|
674
|
+
* The statements to be deleted and inserted may span more than one web document.
|
|
675
|
+
* @param deletions - Statement or statements to be deleted.
|
|
676
|
+
* @param insertions - Statement or statements to be inserted.
|
|
677
|
+
* @returns a promise
|
|
678
|
+
*/
|
|
679
|
+
updateMany(deletions) {
|
|
680
|
+
let insertions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
681
|
+
const docs = deletions.concat(insertions).map(st => st.why);
|
|
682
|
+
const thisUpdater = this;
|
|
683
|
+
const uniqueDocs = [];
|
|
684
|
+
docs.forEach(doc => {
|
|
685
|
+
if (!uniqueDocs.find(uniqueDoc => uniqueDoc.equals(doc))) uniqueDocs.push(doc);
|
|
686
|
+
});
|
|
687
|
+
const updates = uniqueDocs.map(doc => thisUpdater.update(deletions.filter(st => st.why.equals(doc)), insertions.filter(st => st.why.equals(doc))));
|
|
688
|
+
if (updates.length > 1) {
|
|
689
|
+
// console.log(`@@ updateMany to ${updates.length}: ${uniqueDocs}`)
|
|
814
690
|
}
|
|
691
|
+
return Promise.all(updates);
|
|
692
|
+
}
|
|
815
693
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
if (
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
query +=
|
|
834
|
-
for (var i = 0; i < ds.length; i++) {
|
|
835
|
-
query += this.anonymizeNT(ds[i]) + '\n';
|
|
836
|
-
}
|
|
837
|
-
query += ' }\n';
|
|
694
|
+
/**
|
|
695
|
+
* @private
|
|
696
|
+
*
|
|
697
|
+
* This helper function constructs SPARQL Update query from resolved arguments.
|
|
698
|
+
*
|
|
699
|
+
* @param ds: deletions array.
|
|
700
|
+
* @param is: insertions array.
|
|
701
|
+
* @param bnodes_context: Additional context to uniquely identify any blank nodes.
|
|
702
|
+
*/
|
|
703
|
+
constructSparqlUpdateQuery(ds, is, bnodes_context) {
|
|
704
|
+
var whereClause = this.contextWhere(bnodes_context);
|
|
705
|
+
var query = '';
|
|
706
|
+
if (whereClause.length) {
|
|
707
|
+
// Is there a WHERE clause?
|
|
708
|
+
if (ds.length) {
|
|
709
|
+
query += 'DELETE { ';
|
|
710
|
+
for (let i = 0; i < ds.length; i++) {
|
|
711
|
+
query += this.anonymizeNT(ds[i]) + '\n';
|
|
838
712
|
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
query += '
|
|
713
|
+
query += ' }\n';
|
|
714
|
+
}
|
|
715
|
+
if (is.length) {
|
|
716
|
+
query += 'INSERT { ';
|
|
717
|
+
for (let i = 0; i < is.length; i++) {
|
|
718
|
+
query += this.anonymizeNT(is[i]) + '\n';
|
|
845
719
|
}
|
|
846
|
-
query +=
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
query +=
|
|
720
|
+
query += ' }\n';
|
|
721
|
+
}
|
|
722
|
+
query += whereClause;
|
|
723
|
+
} else {
|
|
724
|
+
// no where clause
|
|
725
|
+
if (ds.length) {
|
|
726
|
+
query += 'DELETE DATA { ';
|
|
727
|
+
for (let i = 0; i < ds.length; i++) {
|
|
728
|
+
query += this.anonymizeNT(ds[i]) + '\n';
|
|
855
729
|
}
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
query += '
|
|
730
|
+
query += ' } \n';
|
|
731
|
+
}
|
|
732
|
+
if (is.length) {
|
|
733
|
+
if (ds.length) query += ' ; ';
|
|
734
|
+
query += 'INSERT DATA { ';
|
|
735
|
+
for (let i = 0; i < is.length; i++) {
|
|
736
|
+
query += this.nTriples(is[i]) + '\n';
|
|
863
737
|
}
|
|
738
|
+
query += ' }\n';
|
|
864
739
|
}
|
|
865
|
-
return query;
|
|
866
740
|
}
|
|
741
|
+
return query;
|
|
742
|
+
}
|
|
867
743
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
}).join('\n '), "\n };");
|
|
897
|
-
}
|
|
898
|
-
query += " a solid:InsertDeletePatch .\n";
|
|
899
|
-
return query;
|
|
744
|
+
/**
|
|
745
|
+
* @private
|
|
746
|
+
*
|
|
747
|
+
* This helper function constructs n3-patch query from resolved arguments.
|
|
748
|
+
*
|
|
749
|
+
* @param ds: deletions array.
|
|
750
|
+
* @param is: insertions array.
|
|
751
|
+
* @param bnodes_context: Additional context to uniquely identify any blanknodes.
|
|
752
|
+
*/
|
|
753
|
+
constructN3PatchQuery(ds, is, bnodes_context) {
|
|
754
|
+
var query = `
|
|
755
|
+
@prefix solid: <http://www.w3.org/ns/solid/terms#>.
|
|
756
|
+
@prefix ex: <http://www.example.org/terms#>.
|
|
757
|
+
|
|
758
|
+
_:patch
|
|
759
|
+
`;
|
|
760
|
+
// If bnode context is non trivial, express it as ?conditions formula.
|
|
761
|
+
if (bnodes_context && bnodes_context.length > 0) {
|
|
762
|
+
query += `
|
|
763
|
+
solid:where {
|
|
764
|
+
${bnodes_context.map(x => this.anonymizeNT(x)).join('\n ')}
|
|
765
|
+
};`;
|
|
766
|
+
}
|
|
767
|
+
if (ds.length > 0) {
|
|
768
|
+
query += `
|
|
769
|
+
solid:deletes {
|
|
770
|
+
${ds.map(x => this.anonymizeNT(x)).join('\n ')}
|
|
771
|
+
};`;
|
|
900
772
|
}
|
|
773
|
+
if (is.length > 0) {
|
|
774
|
+
query += `
|
|
775
|
+
solid:inserts {
|
|
776
|
+
${is.map(x => this.anonymizeNT(x)).join('\n ')}
|
|
777
|
+
};`;
|
|
778
|
+
}
|
|
779
|
+
query += " a solid:InsertDeletePatch .\n";
|
|
780
|
+
return query;
|
|
781
|
+
}
|
|
901
782
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
var
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
}, secondTry, options); // callbackFunction
|
|
928
|
-
}); // promise
|
|
929
|
-
} // if
|
|
783
|
+
/**
|
|
784
|
+
* This high-level function updates the local store if the web is changed successfully.
|
|
785
|
+
* Deletions, insertions may be undefined or single statements or lists or formulae (may contain bnodes which can be indirectly identified by a where clause).
|
|
786
|
+
* The `why` property of each statement must be the same and give the web document to be updated.
|
|
787
|
+
* @param deletions - Statement or statements to be deleted.
|
|
788
|
+
* @param insertions - Statement or statements to be inserted.
|
|
789
|
+
* @param callback - called as callbackFunction(uri, success, errorbody)
|
|
790
|
+
* OR returns a promise
|
|
791
|
+
* @param options - Options for the fetch call
|
|
792
|
+
*/
|
|
793
|
+
update(deletions, insertions, callback, secondTry) {
|
|
794
|
+
let options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
|
|
795
|
+
if (!callback) {
|
|
796
|
+
var thisUpdater = this;
|
|
797
|
+
return new Promise(function (resolve, reject) {
|
|
798
|
+
// Promise version
|
|
799
|
+
thisUpdater.update(deletions, insertions, function (uri, ok, errorBody) {
|
|
800
|
+
if (!ok) {
|
|
801
|
+
reject(new Error(errorBody));
|
|
802
|
+
} else {
|
|
803
|
+
resolve();
|
|
804
|
+
}
|
|
805
|
+
}, secondTry, options); // callbackFunction
|
|
806
|
+
}); // promise
|
|
807
|
+
} // if
|
|
930
808
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
809
|
+
try {
|
|
810
|
+
var kb = this.store;
|
|
811
|
+
var ds = !deletions ? [] : (0, _terms.isStore)(deletions) ? deletions.statements : deletions instanceof Array ? deletions : [deletions];
|
|
812
|
+
var is = !insertions ? [] : (0, _terms.isStore)(insertions) ? insertions.statements : insertions instanceof Array ? insertions : [insertions];
|
|
813
|
+
if (!(ds instanceof Array)) {
|
|
814
|
+
throw new Error('Type Error ' + typeof ds + ': ' + ds);
|
|
815
|
+
}
|
|
816
|
+
if (!(is instanceof Array)) {
|
|
817
|
+
throw new Error('Type Error ' + typeof is + ': ' + is);
|
|
818
|
+
}
|
|
819
|
+
if (ds.length === 0 && is.length === 0) {
|
|
820
|
+
return callback(null, true); // success -- nothing needed to be done.
|
|
821
|
+
}
|
|
822
|
+
var doc = ds.length ? ds[0].graph : is[0].graph;
|
|
823
|
+
if (!doc) {
|
|
824
|
+
let message = 'Error patching: statement does not specify which document to patch:' + ds[0] + ', ' + is[0];
|
|
825
|
+
// console.log(message)
|
|
826
|
+
throw new Error(message);
|
|
827
|
+
}
|
|
828
|
+
if (doc.termType !== 'NamedNode') {
|
|
829
|
+
let message = 'Error patching: document not a NamedNode:' + ds[0] + ', ' + is[0];
|
|
830
|
+
// console.log(message)
|
|
831
|
+
throw new Error(message);
|
|
832
|
+
}
|
|
833
|
+
var control = this.patchControlFor(doc);
|
|
834
|
+
var startTime = Date.now();
|
|
835
|
+
var props = ['subject', 'predicate', 'object', 'why'];
|
|
836
|
+
var verbs = ['insert', 'delete'];
|
|
837
|
+
var clauses = {
|
|
838
|
+
'delete': ds,
|
|
839
|
+
'insert': is
|
|
840
|
+
};
|
|
841
|
+
verbs.map(function (verb) {
|
|
842
|
+
clauses[verb].map(function (st) {
|
|
843
|
+
if (!doc.equals(st.graph)) {
|
|
844
|
+
throw new Error('update: destination ' + doc + ' inconsistent with delete quad ' + st.graph);
|
|
845
|
+
}
|
|
846
|
+
props.map(function (prop) {
|
|
847
|
+
if (typeof st[prop] === 'undefined') {
|
|
848
|
+
throw new Error('update: undefined ' + prop + ' of statement.');
|
|
967
849
|
}
|
|
968
|
-
props.map(function (prop) {
|
|
969
|
-
if (typeof st[prop] === 'undefined') {
|
|
970
|
-
throw new Error('update: undefined ' + prop + ' of statement.');
|
|
971
|
-
}
|
|
972
|
-
});
|
|
973
850
|
});
|
|
974
851
|
});
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
852
|
+
});
|
|
853
|
+
var protocol = this.editable(doc.value, kb);
|
|
854
|
+
if (protocol === false) {
|
|
855
|
+
throw new Error('Update: Can\'t make changes in uneditable ' + doc);
|
|
856
|
+
}
|
|
857
|
+
if (protocol === undefined) {
|
|
858
|
+
// Not enough metadata
|
|
859
|
+
if (secondTry) {
|
|
860
|
+
throw new Error('Update: Loaded ' + doc + "but still can't figure out what editing protocol it supports.");
|
|
978
861
|
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
862
|
+
// console.log(`Update: have not loaded ${doc} before: loading now...`);
|
|
863
|
+
this.store.fetcher.load(doc).then(response => {
|
|
864
|
+
this.update(deletions, insertions, callback, true, options);
|
|
865
|
+
}, err => {
|
|
866
|
+
if (err.response.status === 404) {
|
|
867
|
+
// nonexistent files are fine
|
|
868
|
+
this.update(deletions, insertions, callback, true, options);
|
|
869
|
+
} else {
|
|
870
|
+
throw new Error(`Update: Can't get updatability status ${doc} before patching: ${err}`);
|
|
983
871
|
}
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
_this4.update(deletions, insertions, callback, true, options);
|
|
991
|
-
} else {
|
|
992
|
-
throw new Error("Update: Can't get updatability status ".concat(doc, " before patching: ").concat(err));
|
|
993
|
-
}
|
|
994
|
-
});
|
|
995
|
-
return;
|
|
996
|
-
} else if (protocol.indexOf('SPARQL') >= 0 || protocol.indexOf('N3PATCH') >= 0) {
|
|
997
|
-
var isSparql = protocol.indexOf('SPARQL') >= 0;
|
|
998
|
-
var bnodes = [];
|
|
999
|
-
// change ReadOnly type to Mutable type
|
|
872
|
+
});
|
|
873
|
+
return;
|
|
874
|
+
} else if (protocol.indexOf('SPARQL') >= 0 || protocol.indexOf('N3PATCH') >= 0) {
|
|
875
|
+
var isSparql = protocol.indexOf('SPARQL') >= 0;
|
|
876
|
+
var bnodes = [];
|
|
877
|
+
// change ReadOnly type to Mutable type
|
|
1000
878
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
879
|
+
if (ds.length) bnodes = this.statementArrayBnodes(ds);
|
|
880
|
+
if (is.length) bnodes = bnodes.concat(this.statementArrayBnodes(is));
|
|
881
|
+
var context = this.bnodeContext(bnodes, doc);
|
|
882
|
+
var query = isSparql ? this.constructSparqlUpdateQuery(ds, is, context) : this.constructN3PatchQuery(ds, is, context);
|
|
883
|
+
options.contentType = isSparql ? 'application/sparql-update' : 'text/n3';
|
|
1006
884
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
try {
|
|
1021
|
-
kb.remove(ds);
|
|
1022
|
-
} catch (e) {
|
|
1023
|
-
success = false;
|
|
1024
|
-
body = 'Remote Ok BUT error deleting ' + ds.length + ' from store!!! ' + e;
|
|
1025
|
-
} // Add in any case -- help recover from weirdness??
|
|
1026
|
-
for (var i = 0; i < is.length; i++) {
|
|
1027
|
-
kb.add(is[i].subject, is[i].predicate, is[i].object, doc);
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
callback(uri, success, body, response);
|
|
1031
|
-
control.pendingUpstream -= 1;
|
|
1032
|
-
// When upstream patches have been sent, reload state if downstream waiting
|
|
1033
|
-
if (control.pendingUpstream === 0 && control.downstreamAction) {
|
|
1034
|
-
var downstreamAction = control.downstreamAction;
|
|
1035
|
-
delete control.downstreamAction;
|
|
1036
|
-
// console.log('delayed downstream action:')
|
|
1037
|
-
downstreamAction(doc);
|
|
1038
|
-
}
|
|
1039
|
-
}, options);
|
|
1040
|
-
} else if (protocol.indexOf('DAV') >= 0) {
|
|
1041
|
-
this.updateDav(doc, ds, is, callback, options);
|
|
1042
|
-
} else {
|
|
1043
|
-
if (protocol.indexOf('LOCALFILE') >= 0) {
|
|
885
|
+
// Track pending upstream patches until they have finished their callbackFunction
|
|
886
|
+
control.pendingUpstream = control.pendingUpstream ? control.pendingUpstream + 1 : 1;
|
|
887
|
+
if ('upstreamCount' in control) {
|
|
888
|
+
control.upstreamCount += 1; // count changes we originated ourselves
|
|
889
|
+
// console.log('upstream count up to : ' + control.upstreamCount)
|
|
890
|
+
}
|
|
891
|
+
this.fire(doc.value, query, (uri, success, body, response) => {
|
|
892
|
+
response.elapsedTimeMs = Date.now() - startTime;
|
|
893
|
+
/* console.log(' UpdateManager: Return ' +
|
|
894
|
+
(success ? 'success ' : 'FAILURE ') + (response as Response).status +
|
|
895
|
+
' elapsed ' + (response as any).elapsedTimeMs + 'ms')
|
|
896
|
+
*/
|
|
897
|
+
if (success) {
|
|
1044
898
|
try {
|
|
1045
|
-
|
|
899
|
+
kb.remove(ds);
|
|
1046
900
|
} catch (e) {
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
901
|
+
success = false;
|
|
902
|
+
body = 'Remote Ok BUT error deleting ' + ds.length + ' from store!!! ' + e;
|
|
903
|
+
} // Add in any case -- help recover from weirdness??
|
|
904
|
+
for (let i = 0; i < is.length; i++) {
|
|
905
|
+
kb.add(is[i].subject, is[i].predicate, is[i].object, doc);
|
|
1050
906
|
}
|
|
1051
|
-
} else {
|
|
1052
|
-
throw new Error("Unhandled edit method: '" + protocol + "' for " + doc);
|
|
1053
907
|
}
|
|
908
|
+
callback(uri, success, body, response);
|
|
909
|
+
control.pendingUpstream -= 1;
|
|
910
|
+
// When upstream patches have been sent, reload state if downstream waiting
|
|
911
|
+
if (control.pendingUpstream === 0 && control.downstreamAction) {
|
|
912
|
+
var downstreamAction = control.downstreamAction;
|
|
913
|
+
delete control.downstreamAction;
|
|
914
|
+
// console.log('delayed downstream action:')
|
|
915
|
+
downstreamAction(doc);
|
|
916
|
+
}
|
|
917
|
+
}, options);
|
|
918
|
+
} else if (protocol.indexOf('DAV') >= 0) {
|
|
919
|
+
this.updateDav(doc, ds, is, callback, options);
|
|
920
|
+
} else {
|
|
921
|
+
if (protocol.indexOf('LOCALFILE') >= 0) {
|
|
922
|
+
try {
|
|
923
|
+
this.updateLocalFile(doc, ds, is, callback, options);
|
|
924
|
+
} catch (e) {
|
|
925
|
+
callback(doc.value, false, 'Exception trying to write back file <' + doc.value + '>\n'
|
|
926
|
+
// + tabulator.Util.stackString(e))
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
} else {
|
|
930
|
+
throw new Error("Unhandled edit method: '" + protocol + "' for " + doc);
|
|
1054
931
|
}
|
|
1055
|
-
} catch (e) {
|
|
1056
|
-
callback(undefined, false, 'Exception in update: ' + e + '\n' + Util.stackString(e));
|
|
1057
932
|
}
|
|
933
|
+
} catch (e) {
|
|
934
|
+
callback(undefined, false, 'Exception in update: ' + e + '\n' + Util.stackString(e));
|
|
1058
935
|
}
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
var contentType = kb.the(response, this.ns.httph('content-type')).value;
|
|
936
|
+
}
|
|
937
|
+
updateDav(doc, ds, is, callbackFunction) {
|
|
938
|
+
let options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
|
|
939
|
+
let kb = this.store;
|
|
940
|
+
// The code below is derived from Kenny's UpdateCenter.js
|
|
941
|
+
var request = kb.any(doc, this.ns.link('request'));
|
|
942
|
+
if (!request) {
|
|
943
|
+
throw new Error('No record of our HTTP GET request for document: ' + doc);
|
|
944
|
+
} // should not happen
|
|
945
|
+
var response = kb.any(request, this.ns.link('response'));
|
|
946
|
+
if (!response) {
|
|
947
|
+
return null; // throw "No record HTTP GET response for document: "+doc
|
|
948
|
+
}
|
|
949
|
+
var contentType = kb.the(response, this.ns.httph('content-type')).value;
|
|
1074
950
|
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
951
|
+
// prepare contents of revised document
|
|
952
|
+
let newSts = kb.statementsMatching(undefined, undefined, undefined, doc).slice(); // copy!
|
|
953
|
+
for (let i = 0; i < ds.length; i++) {
|
|
954
|
+
Util.RDFArrayRemove(newSts, ds[i]);
|
|
955
|
+
}
|
|
956
|
+
for (let i = 0; i < is.length; i++) {
|
|
957
|
+
newSts.push(is[i]);
|
|
958
|
+
}
|
|
959
|
+
const documentString = this.serialize(doc.value, newSts, contentType);
|
|
960
|
+
|
|
961
|
+
// Write the new version back
|
|
962
|
+
var candidateTarget = kb.the(response, this.ns.httph('content-location'));
|
|
963
|
+
var targetURI;
|
|
964
|
+
if (candidateTarget) {
|
|
965
|
+
targetURI = (0, _uri.join)(candidateTarget.value, targetURI);
|
|
966
|
+
}
|
|
967
|
+
options.contentType = contentType;
|
|
968
|
+
options.noMeta = true;
|
|
969
|
+
options.body = documentString;
|
|
970
|
+
return kb.fetcher.webOperation('PUT', targetURI, options).then(response => {
|
|
971
|
+
if (!response.ok) {
|
|
972
|
+
throw new Error(response.error);
|
|
1079
973
|
}
|
|
1080
|
-
for (
|
|
1081
|
-
|
|
974
|
+
for (let i = 0; i < ds.length; i++) {
|
|
975
|
+
kb.remove(ds[i]);
|
|
1082
976
|
}
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
// Write the new version back
|
|
1086
|
-
var candidateTarget = kb.the(response, this.ns.httph('content-location'));
|
|
1087
|
-
var targetURI;
|
|
1088
|
-
if (candidateTarget) {
|
|
1089
|
-
targetURI = (0, _uri.join)(candidateTarget.value, targetURI);
|
|
977
|
+
for (let i = 0; i < is.length; i++) {
|
|
978
|
+
kb.add(is[i].subject, is[i].predicate, is[i].object, doc);
|
|
1090
979
|
}
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
throw new Error(response.error);
|
|
1097
|
-
}
|
|
1098
|
-
for (var _i9 = 0; _i9 < ds.length; _i9++) {
|
|
1099
|
-
kb.remove(ds[_i9]);
|
|
1100
|
-
}
|
|
1101
|
-
for (var _i10 = 0; _i10 < is.length; _i10++) {
|
|
1102
|
-
kb.add(is[_i10].subject, is[_i10].predicate, is[_i10].object, doc);
|
|
1103
|
-
}
|
|
1104
|
-
callbackFunction(doc.value, response.ok, response.responseText, response);
|
|
1105
|
-
}).catch(function (err) {
|
|
1106
|
-
callbackFunction(doc.value, false, err.message, err);
|
|
1107
|
-
});
|
|
1108
|
-
}
|
|
980
|
+
callbackFunction(doc.value, response.ok, response.responseText, response);
|
|
981
|
+
}).catch(err => {
|
|
982
|
+
callbackFunction(doc.value, false, err.message, err);
|
|
983
|
+
});
|
|
984
|
+
}
|
|
1109
985
|
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
var kb = this.store;
|
|
1124
|
-
// console.log('Writing back to local file\n')
|
|
986
|
+
/**
|
|
987
|
+
* Likely deprecated, since this lib no longer deals with browser extension
|
|
988
|
+
*
|
|
989
|
+
* @param doc
|
|
990
|
+
* @param ds
|
|
991
|
+
* @param is
|
|
992
|
+
* @param callbackFunction
|
|
993
|
+
* @param options
|
|
994
|
+
*/
|
|
995
|
+
updateLocalFile(doc, ds, is, callbackFunction) {
|
|
996
|
+
let options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
|
|
997
|
+
const kb = this.store;
|
|
998
|
+
// console.log('Writing back to local file\n')
|
|
1125
999
|
|
|
1126
|
-
|
|
1127
|
-
|
|
1000
|
+
// prepare contents of revised document
|
|
1001
|
+
let newSts = kb.statementsMatching(undefined, undefined, undefined, doc).slice(); // copy!
|
|
1128
1002
|
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1003
|
+
for (let i = 0; i < ds.length; i++) {
|
|
1004
|
+
Util.RDFArrayRemove(newSts, ds[i]);
|
|
1005
|
+
}
|
|
1006
|
+
for (let i = 0; i < is.length; i++) {
|
|
1007
|
+
newSts.push(is[i]);
|
|
1008
|
+
}
|
|
1009
|
+
// serialize to the appropriate format
|
|
1010
|
+
var dot = doc.value.lastIndexOf('.');
|
|
1011
|
+
if (dot < 1) {
|
|
1012
|
+
throw new Error('Rewriting file: No filename extension: ' + doc.value);
|
|
1013
|
+
}
|
|
1014
|
+
var ext = doc.value.slice(dot + 1);
|
|
1015
|
+
let contentType = _fetcher.default.CONTENT_TYPE_BY_EXT[ext];
|
|
1016
|
+
if (!contentType) {
|
|
1017
|
+
throw new Error('File extension .' + ext + ' not supported for data write');
|
|
1018
|
+
}
|
|
1019
|
+
options.body = this.serialize(doc.value, newSts, contentType);
|
|
1020
|
+
options.contentType = contentType;
|
|
1021
|
+
kb.fetcher.webOperation('PUT', doc.value, options).then(response => {
|
|
1022
|
+
if (!response.ok) return callbackFunction(doc.value, false, response.error);
|
|
1023
|
+
for (let i = 0; i < ds.length; i++) {
|
|
1024
|
+
kb.remove(ds[i]);
|
|
1139
1025
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
if (!contentType) {
|
|
1143
|
-
throw new Error('File extension .' + ext + ' not supported for data write');
|
|
1026
|
+
for (let i = 0; i < is.length; i++) {
|
|
1027
|
+
kb.add(is[i].subject, is[i].predicate, is[i].object, doc);
|
|
1144
1028
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1029
|
+
callbackFunction(doc.value, true, ''); // success!
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
/**
|
|
1034
|
+
* @throws {Error} On unsupported content type
|
|
1035
|
+
*
|
|
1036
|
+
* @returns {string}
|
|
1037
|
+
*/
|
|
1038
|
+
serialize(uri, data, contentType) {
|
|
1039
|
+
const kb = this.store;
|
|
1040
|
+
let documentString;
|
|
1041
|
+
if (typeof data === 'string') {
|
|
1042
|
+
return data;
|
|
1157
1043
|
}
|
|
1158
1044
|
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1045
|
+
// serialize to the appropriate format
|
|
1046
|
+
var sz = (0, _serializer.default)(kb);
|
|
1047
|
+
sz.suggestNamespaces(kb.namespaces);
|
|
1048
|
+
sz.setBase(uri);
|
|
1049
|
+
switch (contentType) {
|
|
1050
|
+
case 'text/xml':
|
|
1051
|
+
case 'application/rdf+xml':
|
|
1052
|
+
documentString = sz.statementsToXML(data);
|
|
1053
|
+
break;
|
|
1054
|
+
case 'text/n3':
|
|
1055
|
+
case 'text/turtle':
|
|
1056
|
+
case 'application/x-turtle': // Legacy
|
|
1057
|
+
case 'application/n3':
|
|
1058
|
+
// Legacy
|
|
1059
|
+
documentString = sz.statementsToN3(data);
|
|
1060
|
+
break;
|
|
1061
|
+
default:
|
|
1062
|
+
throw new Error('Content-type ' + contentType + ' not supported for data serialization');
|
|
1063
|
+
}
|
|
1064
|
+
return documentString;
|
|
1065
|
+
}
|
|
1172
1066
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
break;
|
|
1189
|
-
default:
|
|
1190
|
-
throw new Error('Content-type ' + contentType + ' not supported for data serialization');
|
|
1067
|
+
/**
|
|
1068
|
+
* This is suitable for an initial creation of a document.
|
|
1069
|
+
*/
|
|
1070
|
+
put(doc, data, contentType, callback) {
|
|
1071
|
+
const kb = this.store;
|
|
1072
|
+
let documentString;
|
|
1073
|
+
return Promise.resolve().then(() => {
|
|
1074
|
+
documentString = this.serialize(doc.value, data, contentType);
|
|
1075
|
+
return kb.fetcher.webOperation('PUT', doc.value, {
|
|
1076
|
+
contentType,
|
|
1077
|
+
body: documentString
|
|
1078
|
+
});
|
|
1079
|
+
}).then(response => {
|
|
1080
|
+
if (!response.ok) {
|
|
1081
|
+
return callback(doc.value, response.ok, response.error, response);
|
|
1191
1082
|
}
|
|
1192
|
-
|
|
1193
|
-
|
|
1083
|
+
delete kb.fetcher.nonexistent[doc.value];
|
|
1084
|
+
delete kb.fetcher.requested[doc.value]; // @@ could this mess with the requested state machine? if a fetch is in progress
|
|
1194
1085
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
}, {
|
|
1199
|
-
key: "put",
|
|
1200
|
-
value: function put(doc, data, contentType, callback) {
|
|
1201
|
-
var _this5 = this;
|
|
1202
|
-
var kb = this.store;
|
|
1203
|
-
var documentString;
|
|
1204
|
-
return Promise.resolve().then(function () {
|
|
1205
|
-
documentString = _this5.serialize(doc.value, data, contentType);
|
|
1206
|
-
return kb.fetcher.webOperation('PUT', doc.value, {
|
|
1207
|
-
contentType: contentType,
|
|
1208
|
-
body: documentString
|
|
1086
|
+
if (typeof data !== 'string') {
|
|
1087
|
+
data.map(st => {
|
|
1088
|
+
kb.addStatement(st);
|
|
1209
1089
|
});
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
if (typeof data !== 'string') {
|
|
1218
|
-
data.map(function (st) {
|
|
1219
|
-
kb.addStatement(st);
|
|
1220
|
-
});
|
|
1221
|
-
}
|
|
1222
|
-
callback(doc.value, response.ok, '', response);
|
|
1223
|
-
}).catch(function (err) {
|
|
1224
|
-
callback(doc.value, false, err.message);
|
|
1225
|
-
});
|
|
1226
|
-
}
|
|
1090
|
+
}
|
|
1091
|
+
callback(doc.value, response.ok, '', response);
|
|
1092
|
+
}).catch(err => {
|
|
1093
|
+
callback(doc.value, false, err.message);
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1227
1096
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
doc.reloadTimeTotal += elapsedTimeMs;
|
|
1265
|
-
doc.reloadTimeCount += 1;
|
|
1097
|
+
/**
|
|
1098
|
+
* Reloads a document.
|
|
1099
|
+
*
|
|
1100
|
+
* Fast and cheap, no metadata. Measure times for the document.
|
|
1101
|
+
* Load it provisionally.
|
|
1102
|
+
* Don't delete the statements before the load, or it will leave a broken
|
|
1103
|
+
* document in the meantime.
|
|
1104
|
+
*
|
|
1105
|
+
* @param kb
|
|
1106
|
+
* @param doc {RDFlibNamedNode}
|
|
1107
|
+
* @param callbackFunction
|
|
1108
|
+
*/
|
|
1109
|
+
reload(kb, doc, callbackFunction) {
|
|
1110
|
+
var startTime = Date.now();
|
|
1111
|
+
// force sets no-cache and
|
|
1112
|
+
const options = {
|
|
1113
|
+
force: true,
|
|
1114
|
+
noMeta: true,
|
|
1115
|
+
clearPreviousData: true
|
|
1116
|
+
};
|
|
1117
|
+
kb.fetcher.nowOrWhenFetched(doc.value, options, function (ok, body, response) {
|
|
1118
|
+
if (!ok) {
|
|
1119
|
+
// console.log(' ERROR reloading data: ' + body)
|
|
1120
|
+
callbackFunction(false, 'Error reloading data: ' + body, response);
|
|
1121
|
+
//@ts-ignore Where does onErrorWasCalled come from?
|
|
1122
|
+
} else if (response.onErrorWasCalled || response.status !== 200) {
|
|
1123
|
+
// console.log(' Non-HTTP error reloading data! onErrorWasCalled=' +
|
|
1124
|
+
//@ts-ignore Where does onErrorWasCalled come from?
|
|
1125
|
+
// response.onErrorWasCalled + ' status: ' + response.status)
|
|
1126
|
+
callbackFunction(false, 'Non-HTTP error reloading data: ' + body, response);
|
|
1127
|
+
} else {
|
|
1128
|
+
var elapsedTimeMs = Date.now() - startTime;
|
|
1129
|
+
if (!doc.reloadTimeTotal) doc.reloadTimeTotal = 0;
|
|
1130
|
+
if (!doc.reloadTimeCount) doc.reloadTimeCount = 0;
|
|
1131
|
+
doc.reloadTimeTotal += elapsedTimeMs;
|
|
1132
|
+
doc.reloadTimeCount += 1;
|
|
1266
1133
|
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1134
|
+
// console.log(' Fetch took ' + elapsedTimeMs + 'ms, av. of ' +
|
|
1135
|
+
// doc.reloadTimeCount + ' = ' +
|
|
1136
|
+
// (doc.reloadTimeTotal / doc.reloadTimeCount) + 'ms.')
|
|
1270
1137
|
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1138
|
+
callbackFunction(true);
|
|
1139
|
+
}
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
exports.default = UpdateManager;
|