Skip to content

Commit 7d24e24

Browse files
authored
Merge pull request #512 from linkeddata/flush-metadata
Flush metadata
2 parents 8a42dd5 + e46f792 commit 7d24e24

6 files changed

+132
-35
lines changed

src/fetcher.ts

+9-8
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ export default class Fetcher implements CallbackifyInterface {
721721
_fetch: Fetch
722722
mediatypes: MediatypesMap
723723
/** Denoting this session */
724-
appNode: BlankNode
724+
appNode: NamedNode
725725
/**
726726
* this.requested[uri] states:
727727
* undefined no record of web access or records reset
@@ -771,8 +771,9 @@ export default class Fetcher implements CallbackifyInterface {
771771
if (!this._fetch) {
772772
throw new Error('No _fetch function available for Fetcher')
773773
}
774-
775-
this.appNode = this.store.rdfFactory.blankNode()
774+
// This is the name of the graph we store all the HTTP metadata in
775+
this.appNode = this.store.sym('chrome://TheCurrentSession')
776+
// this.appNode = this.store.rdfFactory.blankNode() // Needs to have a URI in tests
776777
this.store.fetcher = this // Bi-linked
777778
this.requested = {}
778779
this.timeouts = {}
@@ -1725,22 +1726,22 @@ export default class Fetcher implements CallbackifyInterface {
17251726

17261727
let responseNode = kb.bnode()
17271728

1728-
kb.add(options.req, this.ns.link('response'), responseNode, responseNode)
1729+
kb.add(options.req, this.ns.link('response'), responseNode, this.appNode)
17291730
kb.add(responseNode, this.ns.http('status'),
1730-
kb.rdfFactory.literal(response.status as any), responseNode)
1731+
kb.rdfFactory.literal(response.status as any), this.appNode)
17311732
kb.add(responseNode, this.ns.http('statusText'),
1732-
kb.rdfFactory.literal(response.statusText), responseNode)
1733+
kb.rdfFactory.literal(response.statusText), this.appNode)
17331734

17341735
// Save the response headers
17351736
response.headers.forEach((value, header) => {
1736-
kb.add(responseNode, this.ns.httph(header), this.store.rdfFactory.literal(value), responseNode)
1737+
kb.add(responseNode, this.ns.httph(header), this.store.rdfFactory.literal(value), this.appNode)
17371738

17381739
if (header === 'content-type') {
17391740
kb.add(
17401741
options.resource,
17411742
this.ns.rdf('type'),
17421743
kb.rdfFactory.namedNode(Util.mediaTypeClass(value).value),
1743-
responseNode
1744+
this.appNode // responseNode
17441745
)
17451746
}
17461747
})

src/update-manager.ts

+50-5
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,52 @@ export default class UpdateManager {
8181
return( uri.slice(0,4) === 'http' )
8282
}
8383

84+
/** Remove from the store HTTP authorization metadata
85+
* The editble function below relies on copies we have in the store
86+
* of the results of previous HTTP transactions. Howver, when
87+
* the user logs in, then that data misrepresents what would happen
88+
* if the user tried again.
89+
*/
90+
flagAuthorizationMetadata () {
91+
const kb = this.store
92+
const meta = kb.fetcher.appNode
93+
const requests = kb.statementsMatching(undefined, this.ns.link('requestedURI'), undefined, meta).map(st => st.subject)
94+
for (const request of requests) {
95+
const response = kb.any(request, this.ns.link('response'), null, meta) as Quad_Subject
96+
if (response !== undefined) { // ts
97+
this.store.add(response, this.ns.link('outOfDate'), true as any, meta) // @@ Boolean is fine - fix types
98+
}
99+
}
100+
}
84101

102+
/**
103+
* Tests whether a file is editable.
104+
* If the file has a specific annotation that it is machine written,
105+
* for safety, it is editable (this doesn't actually check for write access)
106+
* If the file has wac-allow and accept patch headers, those are respected.
107+
* and local write access is determined by those headers.
108+
* This async version not only looks at past HTTP requests, it also makes new ones if necessary.
109+
*
110+
* @returns The method string SPARQL or DAV or
111+
* LOCALFILE or false if known, undefined if not known.
112+
*/
113+
async checkEditable (uri: string | NamedNode, kb?: IndexedFormula): Promise<string | boolean | undefined> {
114+
const initial = this.editable(uri, kb)
115+
if (initial !== undefined) {
116+
return initial
117+
}
118+
await this.store.fetcher.load(uri)
119+
const final = this.editable(uri, kb)
120+
// console.log(`Loaded ${uri} just to check editable, result: ${final}.`)
121+
return final
122+
}
85123
/**
86124
* Tests whether a file is editable.
87125
* If the file has a specific annotation that it is machine written,
88126
* for safety, it is editable (this doesn't actually check for write access)
89127
* If the file has wac-allow and accept patch headers, those are respected.
90128
* and local write access is determined by those headers.
91-
* This version only looks at past HTTP requests, does not make new ones.
129+
* This synchronous version only looks at past HTTP requests, does not make new ones.
92130
*
93131
* @returns The method string SPARQL or DAV or
94132
* LOCALFILE or false if known, undefined if not known.
@@ -103,7 +141,7 @@ export default class UpdateManager {
103141
uri = termValue(uri)
104142

105143
if ( !this.isHttpUri(uri as string) ) {
106-
if (kb.holds(
144+
if (this.store.holds(
107145
this.store.rdfFactory.namedNode(uri),
108146
this.store.rdfFactory.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
109147
this.store.rdfFactory.namedNode('http://www.w3.org/2007/ont/link#MachineEditableDocument'))) {
@@ -113,14 +151,21 @@ export default class UpdateManager {
113151

114152
var request
115153
var definitive = false
154+
const meta = this.store.fetcher.appNode
155+
// const kb = s
156+
116157
// @ts-ignore passes a string to kb.each, which expects a term. Should this work?
117-
var requests = kb.each(undefined, this.ns.link('requestedURI'), docpart(uri))
158+
var requests = kb.each(undefined, this.ns.link('requestedURI'), docpart(uri), meta)
118159
var method: string
119160
for (var r = 0; r < requests.length; r++) {
120161
request = requests[r]
121162
if (request !== undefined) {
122-
var response = kb.any(request, this.ns.link('response')) as Quad_Subject
123-
if (request !== undefined) {
163+
const response = kb.any(request, this.ns.link('response'), null, meta) as Quad_Subject
164+
if (response !== undefined) { // ts
165+
166+
const outOfDate = kb.anyJS(response, this.ns.link('outOfDate'), null, meta) as Quad_Subject
167+
if (outOfDate) continue
168+
124169
var wacAllow = kb.anyValue(response, this.ns.httph('wac-allow'))
125170
if (wacAllow) {
126171
for (var bit of wacAllow.split(',')) {

tests/unit/fetcher-test.js

+14-15
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ describe('Fetcher', () => {
175175
describe('load', () => {
176176
// let fetcher, uri, options, xhr
177177

178-
it('should load multiple docs')
178+
// it.skip('should load multiple docs', () => {})
179179
})
180180

181181
describe('load', () => {
@@ -338,7 +338,6 @@ describe('Fetcher', () => {
338338
delete fetcher.requested[uri]
339339
return fetcher.webOperation('PATCH', uri, options) // load() is not usable for PATCH
340340
.then(result => {}, () => {
341-
console.log('###### ' + fetcher.requested[uri])
342341
expect(fetcher.requested[uri]).to.not.exist()
343342
})
344343
})
@@ -438,33 +437,33 @@ describe('Fetcher', () => {
438437
})
439438

440439
describe('guessContentType', () => {
441-
it('should return null if uri has no extension')
440+
// it.skip('should return null if uri has no extension')
442441

443-
it('should return null if unknown extension')
442+
// it.skip('should return null if unknown extension')
444443

445-
it('it should return the content type for a known extension')
444+
// it.skip('it should return the content type for a known extension')
446445
})
447446

448447
describe('normalizedContentType', () => {
449-
it('should return the forced content type if present')
448+
// it.skip('should return the forced content type if present')
450449

451-
it('should try to guess content type if none returned in header')
450+
// it.skip('should try to guess content type if none returned in header')
452451

453-
it('should try to guess content type for octet-stream generic type')
452+
// it.skip('should try to guess content type for octet-stream generic type')
454453

455-
it('should return the content type in the headers')
454+
// it.skip('should return the content type in the headers')
456455

457-
it('should default to text/xml for file: protocol uris')
456+
// it.skip('should default to text/xml for file: protocol uris')
458457

459-
it('should default to text/xml for chrome: protocol uris')
458+
// it.skip('should default to text/xml for chrome: protocol uris')
460459
})
461460

462461
describe('handlerForContentType', () => {
463-
it('should return null when no contentType given')
462+
// it.skip('should return null when no contentType given')
464463

465-
it('should return a handler instance if content type matches')
464+
// it.skip('should return a handler instance if content type matches')
466465

467-
it('should return null when no handler match is found')
466+
// it.skip('should return null when no handler match is found')
468467
})
469468

470469
describe('load nock tests', () => {
@@ -568,6 +567,6 @@ describe('Fetcher', () => {
568567
})
569568

570569
describe('createContainer', () => {
571-
it('should invoke webOperation with the right options')
570+
// it.skip('should invoke webOperation with the right options', () => {})
572571
})
573572
})

tests/unit/lists-test.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,13 @@ describe('Lists', () => {
6262
'[ rdf:first 1; rdf:rest [ rdf:first 2; rdf:rest [ rdf:first 3; rdf:rest rdf:nil ]]] <#value> <#test> .'
6363
parse(content, store, base, mimeType)
6464
convertFirstRestNil(store, doc)
65-
console.log('@@@ CCC ' + dumpStore(store))
65+
// console.log('@@@ CCC ' + dumpStore(store))
6666
expect(store.statements[0].subject.termType).to.eql('Collection')
6767
expect(store.statements[0].subject.elements.length).to.eql(3)
6868
})
6969

7070
// Skip for now as serializer does not handle list as subject
71+
/*
7172
it.skip('handles a lone list in Subject position serialized', () => {
7273
let base = 'https://example.com/tests/test.ttl'
7374
let mimeType = 'text/turtle'
@@ -79,14 +80,15 @@ describe('Lists', () => {
7980
// console.log('@@@ AAA ' + showDoc(store, doc))
8081
// expect(store.statements[0].object.termType).to.eql('BlankNode')
8182
convertFirstRestNil(store, doc)
82-
console.log('@@@ CCC ' + dumpStore(store))
83+
// console.log('@@@ CCC ' + dumpStore(store))
8384
// expect(store.statements[0].object.termType).to.eql('Collection')
8485
expect(showDoc(store, doc)).to.eql(`@prefix : <#>.
8586
8687
( 1 2 3 ) :value :test .
8788
8889
`)
8990
})
91+
*/
9092

9193
it('handles nested lists', () => {
9294
let base = 'https://example.com/tests/test.ttl'

tests/unit/live-store-test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const doc = bar.doc()
2727
const doc1 = bar.doc()
2828
const doc2 = baz.doc()
2929

30-
const meta = $rdf.sym(doc.uri + '#meta') // random graph name for meta data
30+
const meta = $rdf.sym('chrome://TheCurrentSession') // graph name for meta data
3131
const st1 = $rdf.st(bar, p, 111, doc)
3232
const st2 = $rdf.st(bar, p, 222, doc1)
3333
const st3 = $rdf.st(baz, p, 333, doc2)

tests/unit/update-manager-test.js

+54-4
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,16 @@ const doc = bar.doc()
2727
const doc1 = bar.doc()
2828
const doc2 = baz.doc()
2929

30-
const meta = $rdf.sym(doc.uri + '#meta') // random graph name for meta data
30+
const meta = $rdf.sym('chrome://TheCurrentSession') // specific graph name for meta data
31+
// const meta = store.fetcher.appNode // random graph name for meta data
3132
const st1 = $rdf.st(bar, p, 111, doc)
3233
const st2 = $rdf.st(bar, p, 222, doc1)
3334
const st3 = $rdf.st(baz, p, 333, doc2)
3435

3536
const httpResultsText = `
3637
@prefix httph: <http://www.w3.org/2007/ont/httph#> .
3738
@prefix link: <http://www.w3.org/2007/ont/link#>.
38-
[] link:requestedURI "${doc.uri}", "${doc2.uri}"; link:response [ httph:ms-author-via "SPARQL" ].
39+
[] link:requestedURI "${doc.uri}", "${doc2.uri}"; link:response [ httph:accept-patch "application/sparql-update" ].
3940
`
4041

4142
function loadMeta (store) {
@@ -155,15 +156,13 @@ describe('UpdateManager', () => {
155156

156157
})
157158

158-
// on console should display @@@@@@@ updateMany to: 2
159159
it('Should insert triples in more than one document', () => {
160160
loadMeta(updater.store)
161161
updater.updateMany([], [st1, st2, st3]).then(array => {
162162
expect(updater.store.fetcher.webOperation).to.have.been.called()
163163
})
164164
})
165165

166-
// on console should display @@@@@@@ updateMany to: 2
167166
it('Should remove triples in more than one document', done => {
168167
loadMeta(updater.store)
169168
updater.updateMany([], [st1, st2, st3])
@@ -198,4 +197,55 @@ describe('UpdateManager', () => {
198197

199198
})
200199

200+
describe('editable', () => {
201+
const self = {err: ''}
202+
let updater, docuri, rterm, options, userCallback, loadStub
203+
var loadStatus = 200
204+
205+
beforeEach(() => {
206+
options = {}
207+
userCallback = () => {}
208+
209+
updater = new UpdateManager()
210+
updater.store.fetcher.webOperation = sinon.stub().resolves({ ok: true, status: 200, statusText: "Dummy stub 1"})
211+
updater.store.fetcher.load = sinon.stub().resolves({ ok: true, status: 200, statusText: "Dummy stub 2"})
212+
213+
})
214+
215+
it('Should detect a document is editable from metadata', () => {
216+
loadMeta(updater.store)
217+
expect(updater.editable(doc1)).to.equal('SPARQL')
218+
})
219+
220+
it('Should not detect a document is editable from metadata after flush', () => {
221+
loadMeta(updater.store)
222+
updater.flagAuthorizationMetadata()
223+
expect(updater.editable(doc1)).to.equal(undefined)
224+
})
225+
226+
227+
it('Async version should detect a document is editable from metadata', async () => {
228+
loadMeta(updater.store)
229+
const result = await updater.checkEditable(doc1)
230+
expect(result).to.equal('SPARQL')
231+
expect(updater.editable(doc1)).to.equal('SPARQL')
232+
})
233+
234+
it('Async version should not detect a document is editable from metadata after flush', async () => {
235+
loadMeta(updater.store)
236+
237+
expect(updater.editable(doc1)).to.equal('SPARQL')
238+
239+
updater.flagAuthorizationMetadata()
240+
241+
const result = await updater.checkEditable(doc1)
242+
243+
expect(result).to.equal(undefined)
244+
})
245+
246+
247+
})
248+
249+
250+
201251
})

0 commit comments

Comments
 (0)