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