sockethub 5.0.0-alpha.0 → 5.0.0-alpha.3
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/bin/sockethub +2 -29
- package/build.js +21 -0
- package/package.json +15 -65
- package/{dist/defaults.json → sockethub.config.json} +8 -7
- package/tsconfig.json +6 -68
- package/README.md +0 -98
- package/config.json.example +0 -32
- package/coverage/tmp/coverage-15699-1646422276150-0.json +0 -1
- package/dist/bootstrap/init.js +0 -74
- package/dist/bootstrap/init.js.map +0 -1
- package/dist/bootstrap/platforms.js +0 -103
- package/dist/common.js +0 -20
- package/dist/common.js.map +0 -1
- package/dist/config.js +0 -60
- package/dist/config.js.map +0 -1
- package/dist/crypto.js +0 -38
- package/dist/crypto.js.map +0 -1
- package/dist/janitor.js +0 -98
- package/dist/janitor.js.map +0 -1
- package/dist/middleware/create-activity-object.js +0 -19
- package/dist/middleware/create-activity-object.js.map +0 -1
- package/dist/middleware/expand-activity-stream.js +0 -33
- package/dist/middleware/expand-activity-stream.js.map +0 -1
- package/dist/middleware/expand-activity-stream.test.data.js +0 -360
- package/dist/middleware/expand-activity-stream.test.data.js.map +0 -1
- package/dist/middleware/store-credentials.js +0 -19
- package/dist/middleware/store-credentials.js.map +0 -1
- package/dist/middleware/validate.js +0 -77
- package/dist/middleware/validate.js.map +0 -1
- package/dist/middleware/validate.test.data.js +0 -321
- package/dist/middleware/validate.test.data.js.map +0 -1
- package/dist/middleware.js +0 -54
- package/dist/middleware.js.map +0 -1
- package/dist/platform-instance.js +0 -226
- package/dist/platform-instance.js.map +0 -1
- package/dist/platform.js +0 -186
- package/dist/platform.js.map +0 -1
- package/dist/process-manager.js +0 -78
- package/dist/process-manager.js.map +0 -1
- package/dist/routes.js +0 -84
- package/dist/routes.js.map +0 -1
- package/dist/serve.js +0 -102
- package/dist/serve.js.map +0 -1
- package/dist/sockethub-client.js +0 -2604
- package/dist/sockethub-client.js.map +0 -1
- package/dist/sockethub-client.min.js +0 -2
- package/dist/sockethub-client.min.js.LICENSE.txt +0 -24
- package/dist/sockethub.js +0 -130
- package/dist/sockethub.js.map +0 -1
- package/dist/store.js +0 -17
- package/dist/store.js.map +0 -1
- package/src/bootstrap/init.d.ts +0 -8
- package/src/bootstrap/init.ts +0 -91
- package/src/bootstrap/platforms.js +0 -103
- package/src/common.test.ts +0 -54
- package/src/common.ts +0 -14
- package/src/config.d.ts +0 -2
- package/src/config.test.ts +0 -28
- package/src/config.ts +0 -72
- package/src/crypto.d.ts +0 -5
- package/src/crypto.test.ts +0 -41
- package/src/crypto.ts +0 -41
- package/src/defaults.json +0 -32
- package/src/janitor.d.ts +0 -8
- package/src/janitor.ts +0 -89
- package/src/middleware/create-activity-object.test.ts +0 -10
- package/src/middleware/create-activity-object.ts +0 -13
- package/src/middleware/expand-activity-stream.test.data.ts +0 -365
- package/src/middleware/expand-activity-stream.test.ts +0 -78
- package/src/middleware/expand-activity-stream.ts +0 -27
- package/src/middleware/store-credentials.test.ts +0 -72
- package/src/middleware/store-credentials.ts +0 -16
- package/src/middleware/validate.d.ts +0 -1
- package/src/middleware/validate.test.data.ts +0 -320
- package/src/middleware/validate.test.ts +0 -47
- package/src/middleware/validate.ts +0 -49
- package/src/middleware.d.ts +0 -21
- package/src/middleware.test.ts +0 -148
- package/src/middleware.ts +0 -52
- package/src/platform-instance.test.ts +0 -237
- package/src/platform-instance.ts +0 -236
- package/src/platform.ts +0 -189
- package/src/process-manager.ts +0 -64
- package/src/routes.test.ts +0 -100
- package/src/routes.ts +0 -93
- package/src/serve.ts +0 -79
- package/src/sockethub-client.test.ts +0 -235
- package/src/sockethub-client.ts +0 -164
- package/src/sockethub.d.ts +0 -1
- package/src/sockethub.ts +0 -184
- package/src/store.test.ts +0 -26
- package/src/store.ts +0 -17
- package/test/init-suite.js +0 -41
- package/test/sockethub-suite.js +0 -25
- package/views/examples/dummy.ejs +0 -93
- package/views/examples/feeds.ejs +0 -90
- package/views/examples/irc.ejs +0 -239
- package/views/examples/shared.js +0 -72
- package/views/examples/xmpp.ejs +0 -191
- package/views/index.ejs +0 -17
- package/webpack.minified.config.js +0 -14
- package/webpack.normal.config.js +0 -14
package/src/middleware.test.ts
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import { expect } from 'chai';
|
|
2
|
-
import * as sinon from 'sinon';
|
|
3
|
-
|
|
4
|
-
import middleware, { MiddlewareChain } from "./middleware";
|
|
5
|
-
|
|
6
|
-
describe("Middleware", () => {
|
|
7
|
-
it("middleware() is a function", () => {
|
|
8
|
-
expect(typeof middleware).to.be.equal('function');
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it('returns a MiddlewareChain instance', () => {
|
|
12
|
-
const mw = middleware('testa');
|
|
13
|
-
expect(mw).to.be.instanceof(MiddlewareChain);
|
|
14
|
-
const mwc = new MiddlewareChain('testa');
|
|
15
|
-
expect(mw.name).to.be.eql(mwc.name);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("only accepts functions", () => {
|
|
19
|
-
const mw = middleware('test');
|
|
20
|
-
// @ts-ignore
|
|
21
|
-
expect(()=>{mw.use('foobar');}).to.throw(
|
|
22
|
-
'middleware use() can only take a function as an argument');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("only accepts functions that expect 2 or 3 params", () => {
|
|
26
|
-
const mw = middleware('test');
|
|
27
|
-
// @ts-ignore
|
|
28
|
-
expect(()=>{mw.use((one, two, three, four) => {});}).to.throw(
|
|
29
|
-
'middleware function provided with incorrect number of params: 4');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("calls each member of call list", (done) => {
|
|
33
|
-
const callback = (data, cb) => { cb(data); };
|
|
34
|
-
const funcs = [ sinon.spy(callback), sinon.spy(callback), sinon.spy(callback) ];
|
|
35
|
-
const mw = middleware('test');
|
|
36
|
-
for (let func of funcs) {
|
|
37
|
-
mw.use(func);
|
|
38
|
-
}
|
|
39
|
-
mw.done()('some data', (data) => {
|
|
40
|
-
expect(data).to.eql('some data');
|
|
41
|
-
funcs.unshift(callback);
|
|
42
|
-
for (let i = 1; i < funcs.length; i++) {
|
|
43
|
-
expect(funcs[i].calledOnce).to.be.true;
|
|
44
|
-
expect(funcs[i].calledWith('foobar', funcs[i - 1]));
|
|
45
|
-
}
|
|
46
|
-
done();
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("does not throw exception on error with no callback provided", (done) => {
|
|
51
|
-
let errorHandlerCalled = false;
|
|
52
|
-
const callbackError = (data, cb) => {
|
|
53
|
-
cb(new Error('some error')); };
|
|
54
|
-
const funcs = [ sinon.spy(callbackError) ];
|
|
55
|
-
const mw = middleware('test');
|
|
56
|
-
for (let func of funcs) {
|
|
57
|
-
const entry = mw.use(func);
|
|
58
|
-
}
|
|
59
|
-
mw.use((err, data, next) => {
|
|
60
|
-
expect(err.toString()).to.equal('Error: some error');
|
|
61
|
-
errorHandlerCalled = true;
|
|
62
|
-
next(err);
|
|
63
|
-
});
|
|
64
|
-
mw.done()('foobar', () => {
|
|
65
|
-
expect(errorHandlerCalled).to.be.true;
|
|
66
|
-
done();
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("aborts call stack on error - calls error handler, and callback", (done) => {
|
|
71
|
-
let errorHandlerCalled = false;
|
|
72
|
-
const callback = (data, cb) => { cb(data); };
|
|
73
|
-
const callbackError = (data, cb) => { cb(new Error('some error')); };
|
|
74
|
-
const funcs = [ sinon.spy(callback), sinon.spy(callbackError), sinon.spy(callback) ];
|
|
75
|
-
const mw = middleware('test');
|
|
76
|
-
for (let func of funcs) {
|
|
77
|
-
mw.use(func);
|
|
78
|
-
}
|
|
79
|
-
mw.use((err, data, next) => {
|
|
80
|
-
expect(err.toString()).to.equal('Error: some error');
|
|
81
|
-
errorHandlerCalled = true;
|
|
82
|
-
next(err);
|
|
83
|
-
});
|
|
84
|
-
mw.done()('foobar', (data) => {
|
|
85
|
-
// FIXME -- We need to also handle socket.io callbacks!
|
|
86
|
-
expect(data instanceof Error).to.be.true;
|
|
87
|
-
expect(funcs[0].calledOnce).to.be.true;
|
|
88
|
-
expect(funcs[0].calledWith('foobar', callback));
|
|
89
|
-
expect(funcs[1].calledOnce).to.be.true;
|
|
90
|
-
expect(funcs[1].calledWith('foobar', funcs[0]));
|
|
91
|
-
expect(funcs[2].calledOnce).to.be.false;
|
|
92
|
-
expect(errorHandlerCalled).to.be.true;
|
|
93
|
-
done();
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("error handler receives error and no callback provided", (done) => {
|
|
98
|
-
let errorHandlerCalled = false;
|
|
99
|
-
const callback = (data, cb) => { cb(data); };
|
|
100
|
-
const callbackError = (data, cb) => { cb(new Error('some error')); };
|
|
101
|
-
const funcs = [ sinon.spy(callback), sinon.spy(callback), sinon.spy(callbackError) ];
|
|
102
|
-
const mw = middleware('test');
|
|
103
|
-
for (let func of funcs) {
|
|
104
|
-
mw.use(func);
|
|
105
|
-
}
|
|
106
|
-
mw.use((err, data, next) => {
|
|
107
|
-
expect(err instanceof Error).to.be.true;
|
|
108
|
-
expect(err.toString()).to.equal('Error: some error');
|
|
109
|
-
errorHandlerCalled = true;
|
|
110
|
-
expect(funcs[0].calledOnce).to.be.true;
|
|
111
|
-
expect(funcs[0].calledWith('foobar', callback));
|
|
112
|
-
expect(funcs[1].calledOnce).to.be.true;
|
|
113
|
-
expect(funcs[1].calledWith('foobar', funcs[0]));
|
|
114
|
-
expect(funcs[2].calledOnce).to.be.true;
|
|
115
|
-
expect(funcs[2].calledWith('foobar', funcs[1]));
|
|
116
|
-
expect(errorHandlerCalled).to.be.true;
|
|
117
|
-
setTimeout(done, 0);
|
|
118
|
-
});
|
|
119
|
-
mw.done()('foobar', () => {});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it("calls each member of chain (50)", (done) => {
|
|
123
|
-
let errorHandlerCalled = false;
|
|
124
|
-
const callback = (data, cb) => { cb(data); };
|
|
125
|
-
let funcs = [];
|
|
126
|
-
for (let i = 0; i <= 50; i++) {
|
|
127
|
-
funcs.push(sinon.spy(callback));
|
|
128
|
-
}
|
|
129
|
-
const mw = middleware('test');
|
|
130
|
-
for (let func of funcs) {
|
|
131
|
-
mw.use(func);
|
|
132
|
-
}
|
|
133
|
-
mw.use((err, next, data) => {
|
|
134
|
-
expect(err.toString()).to.equal('Error: some error');
|
|
135
|
-
errorHandlerCalled = true;
|
|
136
|
-
});
|
|
137
|
-
mw.done()('some data', (data) => {
|
|
138
|
-
expect(data).to.equal('some data');
|
|
139
|
-
funcs.unshift(callback);
|
|
140
|
-
for (let i = 1; i < funcs.length; i++) {
|
|
141
|
-
expect(funcs[i].calledOnce).to.be.true;
|
|
142
|
-
expect(funcs[i].calledWith('foo', funcs[i - 1]));
|
|
143
|
-
}
|
|
144
|
-
expect(errorHandlerCalled).to.be.false;
|
|
145
|
-
done();
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
});
|
package/src/middleware.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { debug } from 'debug';
|
|
2
|
-
|
|
3
|
-
export default function middleware(name: string): MiddlewareChain {
|
|
4
|
-
return new MiddlewareChain(name);
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export class MiddlewareChain {
|
|
8
|
-
public name: string;
|
|
9
|
-
private chain: Array<Function> = [];
|
|
10
|
-
private errHandler: Function = (err: Error) => { throw err; };
|
|
11
|
-
private logger: Function;
|
|
12
|
-
|
|
13
|
-
constructor(name: string) {
|
|
14
|
-
this.name = name;
|
|
15
|
-
this.logger = debug(`sockethub:middleware:${name}`);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
use(func: Function): this {
|
|
19
|
-
if (typeof func !== 'function') {
|
|
20
|
-
throw new Error('middleware use() can only take a function as an argument');
|
|
21
|
-
}
|
|
22
|
-
if (func.length === 3) {
|
|
23
|
-
this.errHandler = func;
|
|
24
|
-
} else if (func.length === 2) {
|
|
25
|
-
this.chain.push(func);
|
|
26
|
-
} else {
|
|
27
|
-
throw new Error(
|
|
28
|
-
'middleware function provided with incorrect number of params: ' + func.length);
|
|
29
|
-
}
|
|
30
|
-
return this;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
done() {
|
|
34
|
-
return (data: any, callback: Function) => {
|
|
35
|
-
let position = 0;
|
|
36
|
-
if (typeof callback !== 'function') {
|
|
37
|
-
callback = () => {};
|
|
38
|
-
}
|
|
39
|
-
const next = (_data: any) => {
|
|
40
|
-
if (_data instanceof Error) {
|
|
41
|
-
this.logger(_data);
|
|
42
|
-
this.errHandler(_data, data, callback);
|
|
43
|
-
} else if (typeof this.chain[position] === 'function') {
|
|
44
|
-
this.chain[position++](_data, next);
|
|
45
|
-
} else {
|
|
46
|
-
callback(_data);
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
next(data);
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
}
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
import proxyquire from 'proxyquire';
|
|
2
|
-
import { expect } from 'chai';
|
|
3
|
-
import * as sinon from 'sinon';
|
|
4
|
-
|
|
5
|
-
proxyquire.noPreserveCache();
|
|
6
|
-
proxyquire.noCallThru();
|
|
7
|
-
|
|
8
|
-
const FORK_PATH = __dirname + '/platform.js';
|
|
9
|
-
|
|
10
|
-
describe("PlatformInstance", () => {
|
|
11
|
-
let pi, sandbox, forkFake, socketMock, getSocketFake, PlatformInstance, platformInstances;
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
sandbox = sinon.createSandbox();
|
|
15
|
-
forkFake = sandbox.fake();
|
|
16
|
-
socketMock = {
|
|
17
|
-
emit: sandbox.spy()
|
|
18
|
-
};
|
|
19
|
-
getSocketFake = sinon.fake.resolves(socketMock);
|
|
20
|
-
|
|
21
|
-
const PlatformInstanceMod = proxyquire('./platform-instance', {
|
|
22
|
-
'bull': sandbox.stub().returns({
|
|
23
|
-
on: sandbox.stub()
|
|
24
|
-
}),
|
|
25
|
-
'./store': {
|
|
26
|
-
redisConfig: {
|
|
27
|
-
createClient: () => {}
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
'child_process': {
|
|
31
|
-
fork: forkFake,
|
|
32
|
-
ChildProcess: sandbox.stub()
|
|
33
|
-
},
|
|
34
|
-
'./serve': {
|
|
35
|
-
'io': {
|
|
36
|
-
'in': sandbox.stub().returns({
|
|
37
|
-
fetchSockets: () => {
|
|
38
|
-
return [socketMock];
|
|
39
|
-
}
|
|
40
|
-
})
|
|
41
|
-
},
|
|
42
|
-
getSocket: getSocketFake
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
PlatformInstance = PlatformInstanceMod.default;
|
|
46
|
-
platformInstances = PlatformInstanceMod.platformInstances;
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
afterEach(() => {
|
|
50
|
-
sinon.restore();
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
describe('private instance per-actor', () => {
|
|
54
|
-
it("is set as non-global when an actor is provided", () => {
|
|
55
|
-
const pi = new PlatformInstance({
|
|
56
|
-
identifier: 'id',
|
|
57
|
-
platform: 'name',
|
|
58
|
-
parentId: 'parentId',
|
|
59
|
-
actor: 'actor string'
|
|
60
|
-
});
|
|
61
|
-
expect(pi.global).to.be.equal(false);
|
|
62
|
-
sandbox.assert.calledWith(forkFake, FORK_PATH, ['parentId', 'name', 'id']);
|
|
63
|
-
pi.destroy();
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
describe('PlatformInstance objects', () => {
|
|
68
|
-
beforeEach(() => {
|
|
69
|
-
pi = new PlatformInstance({
|
|
70
|
-
identifier: 'platform identifier',
|
|
71
|
-
platform: 'a platform name',
|
|
72
|
-
parentId: 'the parentId'
|
|
73
|
-
});
|
|
74
|
-
platformInstances.set(pi.id, pi);
|
|
75
|
-
|
|
76
|
-
pi.process = {
|
|
77
|
-
on: sandbox.spy(),
|
|
78
|
-
removeListener: sandbox.spy(),
|
|
79
|
-
removeAllListeners: sandbox.spy(),
|
|
80
|
-
unref: sandbox.spy(),
|
|
81
|
-
kill: sandbox.spy(),
|
|
82
|
-
};
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
afterEach(() => {
|
|
86
|
-
pi.destroy();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('has expected properties', () => {
|
|
90
|
-
expect(typeof PlatformInstance).to.be.equal('function');
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('should have a platformInstances Map', () => {
|
|
94
|
-
expect(platformInstances instanceof Map).to.be.equal(true);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("has certain accessible properties", () => {
|
|
98
|
-
expect(pi.id).to.be.equal('platform identifier');
|
|
99
|
-
expect(pi.name).to.be.equal('a platform name');
|
|
100
|
-
expect(pi.parentId).to.be.equal('the parentId');
|
|
101
|
-
expect(pi.flaggedForTermination).to.be.equal(false);
|
|
102
|
-
expect(pi.global).to.be.equal(true);
|
|
103
|
-
expect(forkFake.calledWith(FORK_PATH, [
|
|
104
|
-
'the parentId', 'a platform name', 'platform identifier'
|
|
105
|
-
])).to.be.ok;
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
describe('registerSession', () => {
|
|
109
|
-
beforeEach(() => {
|
|
110
|
-
pi.callbackFunction = sandbox.fake();
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('adds a close and message handler when a session is registered', () => {
|
|
114
|
-
pi.registerSession('my session id');
|
|
115
|
-
expect(pi.callbackFunction.callCount).to.equal(2);
|
|
116
|
-
sandbox.assert.calledWith(pi.callbackFunction, 'close', 'my session id');
|
|
117
|
-
sandbox.assert.calledWith(pi.callbackFunction, 'message', 'my session id');
|
|
118
|
-
expect(pi.sessions.has('my session id')).to.be.equal(true);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('is able to generate failure reports', () => {
|
|
122
|
-
pi.registerSession('my session id');
|
|
123
|
-
expect(pi.sessions.has('my session id')).to.be.equal(true);
|
|
124
|
-
pi.reportError('my session id', 'an error message');
|
|
125
|
-
pi.sendToClient = sandbox.stub();
|
|
126
|
-
pi.destroy = sandbox.stub();
|
|
127
|
-
expect(pi.sessions.size).to.be.equal(0);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it('initializes the job queue', () => {
|
|
132
|
-
expect(pi.queue).to.be.undefined;
|
|
133
|
-
pi.initQueue('a secret');
|
|
134
|
-
expect(pi.queue).to.be.ok;
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it("cleans up its references when destroyed", async () => {
|
|
138
|
-
pi.initQueue('a secret');
|
|
139
|
-
expect(pi.queue).to.be.ok;
|
|
140
|
-
expect(platformInstances.has('platform identifier')).to.be.true;
|
|
141
|
-
await pi.destroy();
|
|
142
|
-
expect(pi.queue).not.to.be.ok;
|
|
143
|
-
expect(platformInstances.has('platform identifier')).to.be.false;
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it("updates its identifier when changed", () => {
|
|
147
|
-
pi.updateIdentifier('foo bar');
|
|
148
|
-
expect(pi.id).to.be.equal('foo bar');
|
|
149
|
-
expect(platformInstances.has('platform identifier')).to.be.false;
|
|
150
|
-
expect(platformInstances.has('foo bar')).to.be.true;
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('sends messages to client using socket session id', async () => {
|
|
154
|
-
await pi.sendToClient('my session id',
|
|
155
|
-
{foo: 'this is a message object', sessionSecret: 'private data'});
|
|
156
|
-
expect(getSocketFake.callCount).to.equal(1);
|
|
157
|
-
sandbox.assert.calledOnce(getSocketFake);
|
|
158
|
-
sandbox.assert.calledWith(getSocketFake, 'my session id');
|
|
159
|
-
sandbox.assert.calledOnce(socketMock.emit);
|
|
160
|
-
sandbox.assert.calledWith(
|
|
161
|
-
socketMock.emit, 'message', {foo:'this is a message object', context: 'a platform name'});
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('broadcasts to peers', async () => {
|
|
165
|
-
pi.sessions.add('other peer');
|
|
166
|
-
pi.sessions.add('another peer');
|
|
167
|
-
await pi.broadcastToSharedPeers('myself', {foo: 'bar'});
|
|
168
|
-
expect(getSocketFake.callCount).to.equal(2);
|
|
169
|
-
sandbox.assert.calledWith(getSocketFake, 'other peer');
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
describe('handleJobResult', () => {
|
|
173
|
-
beforeEach(() => {
|
|
174
|
-
pi.sendToClient = sandbox.fake();
|
|
175
|
-
pi.broadcastToSharedPeers = sandbox.fake();
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('broadcasts to peers when handling a completed job', async () => {
|
|
179
|
-
pi.sessions.add('other peer');
|
|
180
|
-
await pi.handleJobResult('completed', {msg: {foo: 'bar'}},
|
|
181
|
-
undefined);
|
|
182
|
-
expect(pi.sendToClient.callCount).to.equal(1);
|
|
183
|
-
expect(pi.broadcastToSharedPeers.callCount).to.equal(1);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it('appends completed result message when present', async () => {
|
|
187
|
-
await pi.handleJobResult('completed', {sessionId: 'a session id', msg: {foo: 'bar'}},
|
|
188
|
-
'a good result message');
|
|
189
|
-
expect(pi.broadcastToSharedPeers.callCount).to.equal(1);
|
|
190
|
-
sandbox.assert.calledWith(pi.sendToClient, 'a session id',
|
|
191
|
-
{foo: 'bar'});
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it('appends failed result message when present', async () => {
|
|
195
|
-
await pi.handleJobResult('failed', {sessionId: 'a session id', msg: {foo: 'bar'}},
|
|
196
|
-
'a bad result message');
|
|
197
|
-
expect(pi.broadcastToSharedPeers.callCount).to.equal(1);
|
|
198
|
-
sandbox.assert.calledWith(pi.sendToClient, 'a session id',
|
|
199
|
-
{foo: 'bar', error: 'a bad result message'});
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
describe('callbackFunction', () => {
|
|
204
|
-
beforeEach(() => {
|
|
205
|
-
pi.reportError = sandbox.fake();
|
|
206
|
-
pi.sendToClient = sandbox.fake();
|
|
207
|
-
pi.updateIdentifier = sandbox.fake();
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it('close events from platform thread are reported', () => {
|
|
211
|
-
const close = pi.callbackFunction('close', 'my session id');
|
|
212
|
-
close('error msg');
|
|
213
|
-
sandbox.assert.calledWith(pi.reportError,
|
|
214
|
-
'my session id', 'Error: session thread closed unexpectedly: error msg');
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it('message events from platform thread are route based on command: error', () => {
|
|
218
|
-
const message = pi.callbackFunction('message', 'my session id');
|
|
219
|
-
message(['error', 'error message']);
|
|
220
|
-
sandbox.assert.calledWith(pi.reportError, 'my session id', 'error message');
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it('message events from platform thread are route based on command: updateActor', () => {
|
|
224
|
-
const message = pi.callbackFunction('message', 'my session id');
|
|
225
|
-
message(['updateActor', undefined, {foo: 'bar'}]);
|
|
226
|
-
sandbox.assert.calledWith(pi.updateIdentifier, {foo:'bar'});
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it('message events from platform thread are route based on command: else', () => {
|
|
230
|
-
const message = pi.callbackFunction('message', 'my session id');
|
|
231
|
-
message(['blah', {foo: 'bar'}]);
|
|
232
|
-
sandbox.assert.calledWith(pi.sendToClient,
|
|
233
|
-
'my session id', {foo:'bar'});
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
});
|
package/src/platform-instance.ts
DELETED
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import { ChildProcess, fork } from 'child_process';
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
import { debug, Debugger } from 'debug';
|
|
4
|
-
import Queue from 'bull';
|
|
5
|
-
|
|
6
|
-
import config from "./config";
|
|
7
|
-
import { ActivityStream, JobDataDecrypted, JobEncrypted } from "./sockethub";
|
|
8
|
-
import { getSocket } from "./serve";
|
|
9
|
-
import { decryptJobData } from "./common";
|
|
10
|
-
|
|
11
|
-
// collection of platform instances, stored by `id`
|
|
12
|
-
export const platformInstances = new Map<string, PlatformInstance>();
|
|
13
|
-
|
|
14
|
-
export interface PlatformInstanceParams {
|
|
15
|
-
identifier: string;
|
|
16
|
-
platform: string;
|
|
17
|
-
parentId?: string;
|
|
18
|
-
actor?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface MessageFromPlatform extends Array<string|ActivityStream>{
|
|
22
|
-
0: string, 1: ActivityStream, 2: string}
|
|
23
|
-
export interface MessageFromParent extends Array<string|any>{0: string, 1: any}
|
|
24
|
-
|
|
25
|
-
interface PlatformConfig {
|
|
26
|
-
persist?: boolean;
|
|
27
|
-
requireCredentials?: Array<string>;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export default class PlatformInstance {
|
|
31
|
-
flaggedForTermination: boolean = false;
|
|
32
|
-
id: string;
|
|
33
|
-
queue: Queue;
|
|
34
|
-
completedJobHandlers: Map<string, Function> = new Map();
|
|
35
|
-
config: PlatformConfig = {};
|
|
36
|
-
readonly name: string;
|
|
37
|
-
readonly process: ChildProcess;
|
|
38
|
-
readonly debug: Debugger;
|
|
39
|
-
readonly parentId: string;
|
|
40
|
-
readonly sessions: Set<string> = new Set();
|
|
41
|
-
readonly sessionCallbacks: object = {
|
|
42
|
-
'close': (() => new Map())(),
|
|
43
|
-
'message': (() => new Map())()
|
|
44
|
-
};
|
|
45
|
-
public readonly global?: boolean = false;
|
|
46
|
-
private readonly actor?: string;
|
|
47
|
-
|
|
48
|
-
constructor(params: PlatformInstanceParams) {
|
|
49
|
-
this.id = params.identifier;
|
|
50
|
-
this.name = params.platform;
|
|
51
|
-
this.parentId = params.parentId;
|
|
52
|
-
if (params.actor) {
|
|
53
|
-
this.actor = params.actor;
|
|
54
|
-
} else {
|
|
55
|
-
this.global = true;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
this.debug = debug(`sockethub:platform-instance:${this.id}`);
|
|
59
|
-
// spin off a process
|
|
60
|
-
this.process = fork(join(__dirname, 'platform.js'), [this.parentId, this.name, this.id]);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Destroys all references to this platform instance, internal listeners and controlled processes
|
|
65
|
-
*/
|
|
66
|
-
public async destroy() {
|
|
67
|
-
this.debug(`cleaning up`);
|
|
68
|
-
this.flaggedForTermination = true;
|
|
69
|
-
try {
|
|
70
|
-
await this.queue.removeAllListeners();
|
|
71
|
-
} catch (e) { }
|
|
72
|
-
try {
|
|
73
|
-
await this.queue.obliterate({ force: true });
|
|
74
|
-
} catch (e) { }
|
|
75
|
-
try {
|
|
76
|
-
delete this.queue;
|
|
77
|
-
await this.process.removeAllListeners('close');
|
|
78
|
-
await this.process.unref();
|
|
79
|
-
await this.process.kill();
|
|
80
|
-
} catch (e) { }
|
|
81
|
-
platformInstances.delete(this.id);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* When jobs are completed or failed, we prepare the results and send them to the client socket
|
|
86
|
-
*/
|
|
87
|
-
public initQueue(secret: string) {
|
|
88
|
-
this.queue = new Queue(this.parentId + this.id, { redis: config.get('redis') });
|
|
89
|
-
|
|
90
|
-
this.queue.on('global:completed', (jobId, resultString) => {
|
|
91
|
-
const result = resultString ? JSON.parse(resultString) : "";
|
|
92
|
-
this.queue.getJob(jobId).then(async (job: JobEncrypted) => {
|
|
93
|
-
await this.handleJobResult('completed', decryptJobData(job, secret), result);
|
|
94
|
-
await job.remove();
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
this.queue.on('global:error', (jobId, result) => {
|
|
99
|
-
this.debug("unknown queue error", jobId, result);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
this.queue.on('global:failed', (jobId, result) => {
|
|
103
|
-
this.queue.getJob(jobId).then(async (job: JobEncrypted) => {
|
|
104
|
-
await this.handleJobResult('failed', decryptJobData(job, secret), result);
|
|
105
|
-
await job.remove();
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Register listener to be called when the process emits a message.
|
|
112
|
-
* @param sessionId ID of socket connection that will receive messages from platform emits
|
|
113
|
-
*/
|
|
114
|
-
public registerSession(sessionId: string) {
|
|
115
|
-
if (! this.sessions.has(sessionId)) {
|
|
116
|
-
this.sessions.add(sessionId);
|
|
117
|
-
for (let type of Object.keys(this.sessionCallbacks)) {
|
|
118
|
-
const cb = this.callbackFunction(type, sessionId);
|
|
119
|
-
this.process.on(type, cb);
|
|
120
|
-
this.sessionCallbacks[type].set(sessionId, cb);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Sends a message to client (user), can be registered with an event emitted from the platform
|
|
127
|
-
* process.
|
|
128
|
-
* @param sessionId ID of the socket connection to send the message to
|
|
129
|
-
* @param msg ActivityStream object to send to client
|
|
130
|
-
*/
|
|
131
|
-
public sendToClient(sessionId: string, msg: ActivityStream) {
|
|
132
|
-
getSocket(sessionId).then((socket) => {
|
|
133
|
-
try {
|
|
134
|
-
// this property should never be exposed externally
|
|
135
|
-
delete msg.sessionSecret;
|
|
136
|
-
} catch (e) {}
|
|
137
|
-
msg.context = this.name;
|
|
138
|
-
if ((msg.type === 'error') && (typeof msg.actor === 'undefined') && (this.actor)) {
|
|
139
|
-
// ensure an actor is present if not otherwise defined
|
|
140
|
-
msg.actor = { id: this.actor };
|
|
141
|
-
}
|
|
142
|
-
socket.emit('message', msg);
|
|
143
|
-
}, (err) => this.debug(`sendToClient ${err}`));
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// send message to every connected socket associated with this platform instance.
|
|
147
|
-
private async broadcastToSharedPeers(sessionId: string, msg: ActivityStream) {
|
|
148
|
-
for (let sid of this.sessions.values()) {
|
|
149
|
-
if (sid !== sessionId) {
|
|
150
|
-
this.debug(`broadcasting message to ${sid}`);
|
|
151
|
-
await this.sendToClient(sid, msg);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// handle job results coming in on the queue from platform instances
|
|
157
|
-
private async handleJobResult(type: string, jobData: JobDataDecrypted, result) {
|
|
158
|
-
this.debug(`${type} job ${jobData.title}`);
|
|
159
|
-
delete jobData.msg.sessionSecret;
|
|
160
|
-
let msg = jobData.msg;
|
|
161
|
-
if (type === 'failed') {
|
|
162
|
-
msg.error = result ? result : "job failed for unknown reason";
|
|
163
|
-
if ((this.config.persist) && (this.config.requireCredentials.includes(jobData.msg.type))) {
|
|
164
|
-
this.debug(`critical job type ${jobData.msg.type} failed, terminating platform instance`);
|
|
165
|
-
await this.destroy();
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// send result to client
|
|
170
|
-
const callback = this.completedJobHandlers.get(jobData.title);
|
|
171
|
-
if (callback) {
|
|
172
|
-
callback(msg);
|
|
173
|
-
this.completedJobHandlers.delete(jobData.title);
|
|
174
|
-
} else {
|
|
175
|
-
await this.sendToClient(jobData.sessionId, msg);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// let all related peers know of result as an independent message
|
|
179
|
-
// (not as part of a job completion, or failure)
|
|
180
|
-
await this.broadcastToSharedPeers(jobData.sessionId, msg);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Sends error message to client and clears all references to this class.
|
|
185
|
-
* @param sessionId
|
|
186
|
-
* @param errorMessage
|
|
187
|
-
*/
|
|
188
|
-
private async reportError(sessionId: string, errorMessage: any) {
|
|
189
|
-
const errorObject: ActivityStream = {
|
|
190
|
-
context: this.name,
|
|
191
|
-
type: 'error',
|
|
192
|
-
actor: { id: this.actor },
|
|
193
|
-
error: errorMessage
|
|
194
|
-
};
|
|
195
|
-
this.sendToClient(sessionId, errorObject);
|
|
196
|
-
this.sessions.clear();
|
|
197
|
-
await this.destroy();
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Updates the instance with a new identifier, updating the platformInstances mapping as well.
|
|
202
|
-
* @param identifier
|
|
203
|
-
*/
|
|
204
|
-
private updateIdentifier(identifier: string) {
|
|
205
|
-
platformInstances.delete(this.id);
|
|
206
|
-
this.id = identifier;
|
|
207
|
-
platformInstances.set(this.id, this);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Generates a function tied to a given client session (socket connection), the generated
|
|
212
|
-
* function will be called for each session ID registered, for every platform emit.
|
|
213
|
-
* @param listener
|
|
214
|
-
* @param sessionId
|
|
215
|
-
*/
|
|
216
|
-
private callbackFunction(listener: string, sessionId: string) {
|
|
217
|
-
const funcs = {
|
|
218
|
-
'close': (e: object) => {
|
|
219
|
-
this.debug(`close even triggered ${this.id}: ${e}`);
|
|
220
|
-
this.reportError(sessionId, `Error: session thread closed unexpectedly: ${e}`);
|
|
221
|
-
},
|
|
222
|
-
'message': (data: MessageFromPlatform) => {
|
|
223
|
-
if (data[0] === 'updateActor') {
|
|
224
|
-
// We need to update the key to the store in order to find it in the future.
|
|
225
|
-
this.updateIdentifier(data[2]);
|
|
226
|
-
} else if (data[0] === 'error') {
|
|
227
|
-
this.reportError(sessionId, data[1]);
|
|
228
|
-
} else {
|
|
229
|
-
// treat like a message to clients
|
|
230
|
-
this.sendToClient(sessionId, data[1]);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
return funcs[listener];
|
|
235
|
-
}
|
|
236
|
-
}
|