comparison src/streamed_graph_client.ts @ 36:8b4dc9e87b56

reindent to 2-spaces with prettier
author drewp@bigasterisk.com
date Sat, 28 Dec 2019 02:24:55 -0800
parents 9ec3cbc8791a
children 163b4339804d
comparison
equal deleted inserted replaced
35:29d8ed02a275 36:8b4dc9e87b56
1 // from /my/site/homepage/www/rdf/streamed-graph.js 1 // from /my/site/homepage/www/rdf/streamed-graph.js
2 2
3 import { eachJsonLdQuad } from "./json_ld_quads"; 3 import { eachJsonLdQuad } from "./json_ld_quads";
4 import { N3Store } from 'n3'; 4 import { N3Store } from "n3";
5 import { Store } from 'n3'; 5 import { Store } from "n3";
6 6
7 export class StreamedGraphClient { 7 export class StreamedGraphClient {
8 // holds a n3 Store, which is synced to a server-side 8 // holds a n3 Store, which is synced to a server-side
9 // store that sends patches over SSE 9 // store that sends patches over SSE
10 10
11 onStatus: (msg: string) => void; 11 onStatus: (msg: string) => void;
12 onGraphChanged: () => void; 12 onGraphChanged: () => void;
13 store: N3Store; 13 store: N3Store;
14 events!: EventSource; 14 events!: EventSource;
15 constructor( 15 constructor(
16 eventsUrl: string, 16 eventsUrl: string,
17 onGraphChanged: () => void, 17 onGraphChanged: () => void,
18 onStatus: (status: string) => void, 18 onStatus: (status: string) => void,
19 prefixes: Array<Record<string, string>>, 19 prefixes: Array<Record<string, string>>,
20 staticGraphUrls: Array<string>) { 20 staticGraphUrls: Array<string>
21 console.log('new StreamedGraph', eventsUrl); 21 ) {
22 this.onStatus = onStatus; 22 console.log("new StreamedGraph", eventsUrl);
23 this.onGraphChanged = onGraphChanged; 23 this.onStatus = onStatus;
24 this.onStatus('startup...'); 24 this.onGraphChanged = onGraphChanged;
25 this.onStatus("startup...");
25 26
26 this.store = new Store(); 27 this.store = new Store();
27 28
28 // // Object.keys(prefixes).forEach((prefix) => { 29 // // Object.keys(prefixes).forEach((prefix) => {
29 // // this.store.setPrefix(prefix, prefixes[prefix]); 30 // // this.store.setPrefix(prefix, prefixes[prefix]);
30 // // }); 31 // // });
31 32
32 this.connect(eventsUrl); 33 this.connect(eventsUrl);
33 this.reconnectOnWake(); 34 this.reconnectOnWake();
34 35
35 // staticGraphUrls.forEach((url) => { 36 // staticGraphUrls.forEach((url) => {
36 // fetch(url).then((response) => response.text()) 37 // fetch(url).then((response) => response.text())
37 // .then((body) => { 38 // .then((body) => {
38 // // parse with n3, add to output 39 // // parse with n3, add to output
39 // }); 40 // });
40 // }); 41 // });
42 }
41 43
44 reconnectOnWake() {
45 // it's not this, which fires on every mouse-in on a browser window, and doesn't seem to work for screen-turned-back-on
46 //window.addEventListener('focus', function() { this.connect(eventsUrl); }.bind(this));
47 }
48
49 connect(eventsUrl: string) {
50 // need to exit here if this obj has been replaced
51
52 this.onStatus("start connect...");
53 this.close();
54 if (this.events && this.events.readyState != EventSource.CLOSED) {
55 this.onStatus("zombie");
56 throw new Error("zombie eventsource");
42 } 57 }
43 58
44 reconnectOnWake() { 59 this.events = new EventSource(eventsUrl);
45 // it's not this, which fires on every mouse-in on a browser window, and doesn't seem to work for screen-turned-back-on 60
46 //window.addEventListener('focus', function() { this.connect(eventsUrl); }.bind(this)); 61 this.events.addEventListener("error", ev => {
62 // todo: this is piling up tons of retries and eventually multiple connections
63 // this.testEventUrl(eventsUrl);
64 this.onStatus("connection lost- retrying");
65 setTimeout(() => {
66 requestAnimationFrame(() => {
67 this.connect(eventsUrl);
68 });
69 }, 3000);
70 });
71
72 this.events.addEventListener("fullGraph", async ev => {
73 this.onStatus("sync- full graph update");
74 await this.replaceFullGraph((ev as MessageEvent).data);
75 this.onStatus(`synced ${this.store.size}`);
76 this.onGraphChanged();
77 });
78
79 this.events.addEventListener("patch", async ev => {
80 this.onStatus("sync- updating");
81 await this.patchGraph((ev as MessageEvent).data);
82 this.onStatus(`synced ${this.store.size}`);
83 this.onGraphChanged();
84 });
85 this.onStatus("connecting...");
86 }
87
88 // these need some locks
89 async replaceFullGraph(jsonLdText: string) {
90 this.store = new Store();
91 await eachJsonLdQuad(
92 JSON.parse(jsonLdText),
93 this.store.addQuad.bind(this.store)
94 );
95 }
96
97 async patchGraph(patchJson: string) {
98 var patch = JSON.parse(patchJson).patch;
99
100 await eachJsonLdQuad(patch.deletes, this.store.removeQuad.bind(this.store));
101 await eachJsonLdQuad(patch.adds, this.store.addQuad.bind(this.store));
102 }
103
104 close() {
105 if (this.events) {
106 this.events.close();
47 } 107 }
108 }
48 109
49 connect(eventsUrl: string) { 110 async testEventUrl(eventsUrl: string): Promise<void> {
50 // need to exit here if this obj has been replaced 111 return new Promise<void>((resolve, reject) => {
51 112 this.onStatus("testing connection");
52 this.onStatus('start connect...'); 113 fetch(eventsUrl, {
53 this.close(); 114 method: "HEAD",
54 if (this.events && this.events.readyState != EventSource.CLOSED) { 115 credentials: "include"
55 this.onStatus('zombie'); 116 })
56 throw new Error("zombie eventsource"); 117 .then(value => {
57 } 118 if (value.status == 403) {
58 119 reject();
59 this.events = new EventSource(eventsUrl); 120 return;
60 121 }
61 this.events.addEventListener('error', (ev) => { 122 resolve();
62 // todo: this is piling up tons of retries and eventually multiple connections 123 })
63 // this.testEventUrl(eventsUrl); 124 .catch(err => {
64 this.onStatus('connection lost- retrying'); 125 reject();
65 setTimeout(() => {
66 requestAnimationFrame(() => {
67 this.connect(eventsUrl);
68 });
69 }, 3000);
70 }); 126 });
71 127 });
72 this.events.addEventListener('fullGraph', async (ev) => { 128 }
73 this.onStatus('sync- full graph update');
74 await this.replaceFullGraph((ev as MessageEvent).data);
75 this.onStatus(`synced ${this.store.size}`);
76 this.onGraphChanged();
77 });
78
79 this.events.addEventListener('patch', async (ev) => {
80 this.onStatus('sync- updating');
81 await this.patchGraph((ev as MessageEvent).data);
82 this.onStatus(`synced ${this.store.size}`);
83 this.onGraphChanged();
84 });
85 this.onStatus('connecting...');
86 }
87
88 // these need some locks
89 async replaceFullGraph(jsonLdText: string) {
90 this.store = new Store();
91 await eachJsonLdQuad(JSON.parse(jsonLdText),
92 this.store.addQuad.bind(this.store));
93 }
94
95 async patchGraph(patchJson: string) {
96 var patch = JSON.parse(patchJson).patch;
97
98 await eachJsonLdQuad(patch.deletes,
99 this.store.removeQuad.bind(this.store));
100 await eachJsonLdQuad(patch.adds,
101 this.store.addQuad.bind(this.store));
102 }
103
104 close() {
105 if (this.events) {
106 this.events.close();
107 }
108 }
109
110 async testEventUrl(eventsUrl: string): Promise<void> {
111 return new Promise<void>((resolve, reject) => {
112 this.onStatus('testing connection');
113 fetch(eventsUrl, {
114 method: "HEAD",
115 credentials: "include",
116 }).then((value) => {
117 if (value.status == 403) {
118 reject();
119 return;
120 }
121 resolve();
122 }).catch((err) => {
123 reject();
124 });
125 });
126 }
127
128 } 129 }