sockethub 4.0.1 → 5.0.0-alpha.1

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.
Files changed (139) hide show
  1. package/README.md +3 -3
  2. package/bin/sockethub +23 -19
  3. package/dist/bootstrap/init.js +14 -4
  4. package/dist/bootstrap/init.js.map +1 -1
  5. package/dist/bootstrap/platforms.js +81 -69
  6. package/dist/common.js +10 -12
  7. package/dist/common.js.map +1 -1
  8. package/dist/config.js +4 -22
  9. package/dist/config.js.map +1 -1
  10. package/dist/crypto.js +7 -8
  11. package/dist/crypto.js.map +1 -1
  12. package/dist/janitor.js +14 -9
  13. package/dist/janitor.js.map +1 -1
  14. package/dist/middleware/create-activity-object.js +19 -0
  15. package/dist/middleware/create-activity-object.js.map +1 -0
  16. package/dist/middleware/expand-activity-stream.js +33 -0
  17. package/dist/middleware/expand-activity-stream.js.map +1 -0
  18. package/dist/middleware/expand-activity-stream.test.data.js +360 -0
  19. package/dist/middleware/expand-activity-stream.test.data.js.map +1 -0
  20. package/dist/middleware/store-credentials.js +19 -0
  21. package/dist/middleware/store-credentials.js.map +1 -0
  22. package/dist/middleware/validate.js +77 -0
  23. package/dist/middleware/validate.js.map +1 -0
  24. package/dist/middleware/validate.test.data.js +321 -0
  25. package/dist/middleware/validate.test.data.js.map +1 -0
  26. package/dist/middleware.js +47 -49
  27. package/dist/middleware.js.map +1 -1
  28. package/dist/platform-instance.js +84 -66
  29. package/dist/platform-instance.js.map +1 -1
  30. package/dist/platform.js +50 -25
  31. package/dist/platform.js.map +1 -1
  32. package/dist/process-manager.js +7 -4
  33. package/dist/process-manager.js.map +1 -1
  34. package/dist/routes.js +8 -6
  35. package/dist/routes.js.map +1 -1
  36. package/dist/serve.js +3 -3
  37. package/dist/serve.js.map +1 -1
  38. package/dist/sockethub-client.js +2604 -0
  39. package/dist/sockethub-client.js.map +1 -0
  40. package/dist/sockethub-client.min.js +2 -0
  41. package/dist/sockethub-client.min.js.LICENSE.txt +24 -0
  42. package/dist/sockethub.js +75 -58
  43. package/dist/sockethub.js.map +1 -1
  44. package/dist/store.js +17 -0
  45. package/dist/store.js.map +1 -0
  46. package/package.json +48 -44
  47. package/src/bootstrap/init.ts +16 -2
  48. package/src/bootstrap/platforms.js +14 -18
  49. package/src/common.test.ts +44 -33
  50. package/src/common.ts +9 -17
  51. package/src/config.test.ts +16 -38
  52. package/src/config.ts +1 -23
  53. package/src/crypto.test.ts +15 -17
  54. package/src/crypto.ts +4 -5
  55. package/src/janitor.ts +19 -12
  56. package/src/middleware/create-activity-object.test.ts +10 -0
  57. package/src/middleware/create-activity-object.ts +13 -0
  58. package/src/middleware/expand-activity-stream.test.data.ts +365 -0
  59. package/src/middleware/expand-activity-stream.test.ts +78 -0
  60. package/src/middleware/expand-activity-stream.ts +27 -0
  61. package/src/middleware/store-credentials.test.ts +72 -0
  62. package/src/middleware/store-credentials.ts +16 -0
  63. package/src/{validate.d.ts → middleware/validate.d.ts} +0 -0
  64. package/src/middleware/validate.test.data.ts +320 -0
  65. package/src/middleware/validate.test.ts +47 -0
  66. package/src/middleware/validate.ts +49 -0
  67. package/src/middleware.test.ts +148 -0
  68. package/src/middleware.ts +46 -51
  69. package/src/platform-instance.test.ts +224 -196
  70. package/src/platform-instance.ts +74 -58
  71. package/src/platform.ts +44 -24
  72. package/src/process-manager.ts +7 -4
  73. package/src/routes.test.ts +32 -19
  74. package/src/routes.ts +7 -5
  75. package/src/serve.ts +1 -1
  76. package/src/sockethub-client.test.ts +235 -0
  77. package/src/sockethub-client.ts +164 -0
  78. package/src/sockethub.ts +96 -93
  79. package/src/store.test.ts +26 -0
  80. package/src/store.ts +17 -0
  81. package/tsconfig.json +8 -8
  82. package/views/examples/dummy.ejs +7 -7
  83. package/views/examples/feeds.ejs +10 -10
  84. package/views/examples/irc.ejs +65 -59
  85. package/views/examples/shared.js +31 -29
  86. package/views/examples/xmpp.ejs +49 -58
  87. package/webpack.minified.config.js +14 -0
  88. package/webpack.normal.config.js +14 -0
  89. package/coverage/clover.xml +0 -190
  90. package/coverage/coverage-final.json +0 -6
  91. package/coverage/lcov-report/base.css +0 -224
  92. package/coverage/lcov-report/block-navigation.js +0 -79
  93. package/coverage/lcov-report/common.ts.html +0 -143
  94. package/coverage/lcov-report/config.ts.html +0 -359
  95. package/coverage/lcov-report/crypto.ts.html +0 -203
  96. package/coverage/lcov-report/favicon.png +0 -0
  97. package/coverage/lcov-report/index.html +0 -171
  98. package/coverage/lcov-report/platform-instance.ts.html +0 -740
  99. package/coverage/lcov-report/prettify.css +0 -1
  100. package/coverage/lcov-report/prettify.js +0 -2
  101. package/coverage/lcov-report/routes.ts.html +0 -353
  102. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  103. package/coverage/lcov-report/sorter.js +0 -170
  104. package/coverage/lcov-report/src/common.ts.html +0 -143
  105. package/coverage/lcov-report/src/config.ts.html +0 -359
  106. package/coverage/lcov-report/src/crypto.ts.html +0 -182
  107. package/coverage/lcov-report/src/index.html +0 -156
  108. package/coverage/lcov-report/src/platform-instance.ts.html +0 -740
  109. package/coverage/lcov-report/src/routes/base.ts.html +0 -359
  110. package/coverage/lcov-report/src/routes/examples.ts.html +0 -311
  111. package/coverage/lcov-report/src/routes/index.html +0 -111
  112. package/coverage/lcov-report/src/services/http.ts.html +0 -239
  113. package/coverage/lcov-report/src/services/index.html +0 -111
  114. package/coverage/lcov-report/src/services/redis.ts.html +0 -140
  115. package/coverage/lcov-report/src/shared-resources.ts.html +0 -104
  116. package/coverage/lcov.info +0 -336
  117. package/coverage/tmp/coverage-21649-1621596477257-0.json +0 -1
  118. package/dist/bootstrap/platforms.js.map +0 -1
  119. package/dist/js/client.js +0 -177
  120. package/dist/js/client.js.map +0 -1
  121. package/dist/resource-manager.js +0 -66
  122. package/dist/resource-manager.js.map +0 -1
  123. package/dist/routes/base.js +0 -92
  124. package/dist/routes/base.js.map +0 -1
  125. package/dist/routes/examples.js +0 -93
  126. package/dist/routes/examples.js.map +0 -1
  127. package/dist/services/http.js +0 -67
  128. package/dist/services/http.js.map +0 -1
  129. package/dist/services/redis.js +0 -23
  130. package/dist/services/redis.js.map +0 -1
  131. package/dist/shared-resources.js +0 -11
  132. package/dist/shared-resources.js.map +0 -1
  133. package/dist/validate.js +0 -157
  134. package/dist/validate.js.map +0 -1
  135. package/jest.config.js +0 -18
  136. package/src/js/client.js +0 -190
  137. package/src/validate.ts +0 -147
  138. package/test/middleware-suite.js +0 -101
  139. package/test/validate-suite.js +0 -338
@@ -0,0 +1,320 @@
1
+ export default [
2
+ {
3
+ "name": "mismatched types",
4
+ "valid": false,
5
+ "type": "credentials",
6
+ "input":{
7
+ "id":"blah",
8
+ "type":"send",
9
+ "context":"dummy",
10
+ "actor":{
11
+ "id":"dood@irc.freenode.net",
12
+ "type":"person",
13
+ "name":"dood"
14
+ },
15
+ "target":{
16
+ "id":"irc.freenode.net/sockethub",
17
+ "type":"person",
18
+ "name":"sockethub"
19
+ },
20
+ "object":{
21
+ "type":"credentials",
22
+ "user": 'foo',
23
+ "pass": 'bar'
24
+ }
25
+ },
26
+ 'error': "Error: credential activity streams must have credentials set as type"
27
+ },
28
+ {
29
+ "name": "basic",
30
+ "valid":true,
31
+ "type":"credentials",
32
+ "input":{
33
+ "id":"blah",
34
+ "type":"credentials",
35
+ "context":"dummy",
36
+ "actor":{
37
+ "id":"dood@irc.freenode.net",
38
+ "type":"person",
39
+ "name":"dood"
40
+ },
41
+ "object":{
42
+ 'type': 'credentials',
43
+ "user": 'foo',
44
+ "pass": 'bar'
45
+ }
46
+ },
47
+ "output":"same"
48
+ },
49
+ {
50
+ "name":"irc credentials",
51
+ "valid":true,
52
+ "type":"credentials",
53
+ "input":{
54
+ "context":"irc",
55
+ "type":"credentials",
56
+ "actor":{
57
+ "id":"sh-9K3Vk@irc.freenode.net",
58
+ "type":"person",
59
+ "name":"sh-9K3Vk",
60
+ "image":{
61
+ "height":250,
62
+ "mediaType":"image/jpeg",
63
+ "url":"http://example.org/image.jpg",
64
+ "width":250
65
+ },
66
+ "url":"http://sockethub.org"
67
+ },
68
+ "object":{
69
+ "type":"credentials",
70
+ "nick":"sh-9K3Vk",
71
+ "port":6667,
72
+ "secure":false,
73
+ "server":"irc.freenode.net"
74
+ }
75
+ },
76
+ "output":"same"
77
+ },
78
+ {
79
+ "name":"bad irc credentials: user/nick host/server",
80
+ "valid":false,
81
+ "type":"credentials",
82
+ "input":{
83
+ "context":"irc",
84
+ "type":"credentials",
85
+ "actor":{
86
+ "id":"sh-9K3Vk@irc.freenode.net",
87
+ "type":"person",
88
+ "name":"sh-9K3Vk",
89
+ "image":{
90
+ "height":250,
91
+ "mediaType":"image/jpeg",
92
+ "url":"http://example.org/image.jpg",
93
+ "width":250
94
+ },
95
+ "url":"http://sockethub.org"
96
+ },
97
+ "object":{
98
+ "type":"credentials",
99
+ "user":"sh-9K3Vk",
100
+ "port":6667,
101
+ "secure":false,
102
+ "host":"irc.freenode.net"
103
+ }
104
+ },
105
+ "error":
106
+ "Error: /object: must NOT have additional properties"
107
+ },
108
+ {
109
+ "name":"no type specified",
110
+ "valid":false,
111
+ "type":"credentials",
112
+ "input":{
113
+ "actor":"hyper_rau@localhost",
114
+ "context":"xmpp",
115
+ "object":{
116
+ "username":"hyper_rau",
117
+ "password":"123",
118
+ "server":"localhost",
119
+ "port":5222,
120
+ "resource":"laptop"
121
+ }
122
+ },
123
+ "error": "Error: credential activity streams must have credentials set as type"
124
+ },
125
+ {
126
+ "name":"basic person",
127
+ "type":"activity-object",
128
+ "valid":true,
129
+ "input":{
130
+ "id":"blah",
131
+ "type":"person",
132
+ "name":"dood"
133
+ },
134
+ "output":"same"
135
+ },
136
+ {
137
+ "name":"person with extras",
138
+ "valid":true,
139
+ "type":"activity-object",
140
+ "input":{
141
+ "id":"blah",
142
+ "type":"person",
143
+ "name":"bob",
144
+ "hello":"there",
145
+ "i":[
146
+ "am",
147
+ "extras"
148
+ ]
149
+ },
150
+ "output":"same"
151
+ },
152
+ {
153
+ "name":"alone credentials (as activity-object)",
154
+ "valid":false,
155
+ "type":"activity-object",
156
+ "input":{
157
+ "type":"credentials",
158
+ "nick":"sh-9K3Vk",
159
+ "port":6667,
160
+ "secure":false,
161
+ "server":"irc.freenode.net"
162
+ },
163
+ "error":
164
+ "Error: /object: must match exactly one schema in oneOf: " +
165
+ "credentials, feed, message, me, person, room, service, website, " +
166
+ "attendance, presence, relationship, topic, address"
167
+ },
168
+ {
169
+ "name":"alone credentials (as credentials)",
170
+ "valid":false,
171
+ "type":"credentials",
172
+ "input":{
173
+ "type":"credentials",
174
+ "nick":"sh-9K3Vk",
175
+ "port":6667,
176
+ "secure":false,
177
+ "server":"irc.freenode.net"
178
+ },
179
+ "error": "Error: platform context undefined not registered with this sockethub instance."
180
+ },
181
+ {
182
+ "name":"new person",
183
+ "valid":true,
184
+ "type":"activity-object",
185
+ "input":{
186
+ "id":"sh-9K3Vk@irc.freenode.net",
187
+ "type":"person",
188
+ "name":"sh-9K3Vk",
189
+ "image":{
190
+ "height":250,
191
+ "mediaType":"image/jpeg",
192
+ "url":"http://example.org/image.jpg",
193
+ "width":250
194
+ },
195
+ "url":"http://sockethub.org"
196
+ },
197
+ "output":"same"
198
+ },
199
+ {
200
+ "name":"new person",
201
+ "valid":true,
202
+ "type":"activity-object",
203
+ "input":{
204
+ "id":"irc://sh-9K3Vk@irc.freenode.net",
205
+ "type":"person",
206
+ "name":"sh-9K3Vk",
207
+ "url":"http://sockethub.org"
208
+ },
209
+ "output":"same"
210
+ },
211
+ {
212
+ "name":"bad parent object",
213
+ "valid":false,
214
+ "type":"activity-stream",
215
+ "input":{
216
+ "string":"this is a string",
217
+ "array":[
218
+ "this",
219
+ "is",
220
+ {
221
+ "an":"array"
222
+ }
223
+ ],
224
+ "as":{
225
+ "id":"blah",
226
+ "type":"send",
227
+ "context":"hello",
228
+ "actor":{
229
+ "name":"dood"
230
+ },
231
+ "target":{
232
+ "type":"person",
233
+ "name":"bob"
234
+ },
235
+ "object":{
236
+ "type":"credentials"
237
+ }
238
+ },
239
+ "noId":{
240
+ "name":"dood"
241
+ },
242
+ "noId2":{
243
+ "type":"person",
244
+ "name":"bob"
245
+ },
246
+ "noDisplayName":{
247
+ "id":"larg"
248
+ }
249
+ },
250
+ "error": "Error: platform context undefined not registered with this sockethub instance."
251
+ },
252
+ {
253
+ "name":"unexpected AS",
254
+ "valid":false,
255
+ "type":"message",
256
+ "input":{
257
+ "actor":"irc://uuu@localhost",
258
+ "type":"join",
259
+ "context":"irc",
260
+ "target":"irc://irc.dooder.net/a-room"
261
+ },
262
+ "error": "Error: /actor: must be object"
263
+ },
264
+ {
265
+ "name":"missing type property",
266
+ "valid":false,
267
+ "type":"message",
268
+ "input":{
269
+ "actor": { "id": "irc://uuu@localhost", "type": "person" },
270
+ "context":"irc",
271
+ "target": { "id": "irc://irc.dooder.net/a-room", "type": "room" }
272
+ },
273
+ "error": "Error: activity stream: must have required property \'type\'"
274
+ },
275
+ {
276
+ "name":"invalid context property",
277
+ "valid":false,
278
+ "type":"message",
279
+ "input":{
280
+ "actor": { "id": "irc://uuu@localhost", "type": "person" },
281
+ "type":"foo",
282
+ "context": "foobar",
283
+ "target": { "id": "irc://irc.dooder.net/a-room", "type": "room" }
284
+ },
285
+ "error": "Error: platform context foobar not registered with this sockethub instance."
286
+ },
287
+ {
288
+ "name":"missing actor property",
289
+ "valid":false,
290
+ "type":"message",
291
+ "input":{
292
+ "type": "foo",
293
+ "context":"irc",
294
+ "target": { "id": "irc://irc.dooder.net/a-room", "type": "room" }
295
+ },
296
+ "error": "Error: activity stream: must have required property \'actor\'"
297
+ },
298
+ {
299
+ "name":"traditional message",
300
+ "valid":true,
301
+ "type":"message",
302
+ "input":{
303
+ "type": "update",
304
+ "context": "irc",
305
+ "actor": { "id": "irc://uuu@localhost", "type": "person" }
306
+ }
307
+ },
308
+ {
309
+ "name":"message with wrong type",
310
+ "valid":false,
311
+ "type":"message",
312
+ "input":{
313
+ "type": "foorg",
314
+ "context": "irc",
315
+ "actor": { "id": "irc://uuu@localhost", "type": "person" }
316
+ },
317
+ "error": "Error: platform type foorg not supported by irc platform. " +
318
+ "(types: credentials, connect, update, join, leave, send, query, announce)"
319
+ }
320
+ ];
@@ -0,0 +1,47 @@
1
+ import { expect } from 'chai';
2
+ import proxyquire from 'proxyquire';
3
+
4
+ proxyquire.noPreserveCache();
5
+ proxyquire.noCallThru();
6
+
7
+ // @ts-ignore
8
+ import platformLoad from './../bootstrap/platforms';
9
+ const packageJSON = require('./../../package.json');
10
+ const platforms = platformLoad(Object.keys(packageJSON.dependencies));
11
+
12
+ const validateMod = proxyquire('./validate', {
13
+ '../bootstrap/init': {
14
+ platforms: platforms
15
+ }
16
+ });
17
+ const validate = validateMod.default;
18
+
19
+ import asObjects from "./validate.test.data";
20
+
21
+ describe('Middleware: Validate', () => {
22
+ describe('AS object validations', () => {
23
+ asObjects.forEach((obj) => {
24
+ it(`${obj.type}: ${obj.name}, should ${obj.valid ? 'pass' : 'fail'}`, (done) => {
25
+ // @ts-ignore
26
+ validate(obj.type, 'tests')(obj.input, (msg) => {
27
+ if (obj.output) {
28
+ if (obj.output === 'same') {
29
+ expect(msg).to.eql(obj.input);
30
+ } else {
31
+ expect(msg).to.eql(obj.output);
32
+ }
33
+ }
34
+ if (obj.valid) {
35
+ expect(msg).to.not.be.instanceof(Error);
36
+ } else {
37
+ expect(msg).to.be.instanceof(Error);
38
+ if (obj.error) {
39
+ expect(msg.toString()).to.equal(obj.error);
40
+ }
41
+ }
42
+ done();
43
+ });
44
+ });
45
+ });
46
+ });
47
+ });
@@ -0,0 +1,49 @@
1
+ /**
2
+ * responsible for handling the validation and expansion (when applicable) of all incoming objects
3
+ */
4
+ import debug from 'debug';
5
+ import * as Schemas from 'sockethub-schemas';
6
+ import { ActivityStream } from "../sockethub";
7
+
8
+ // @ts-ignore
9
+ import init from "../bootstrap/init";
10
+
11
+ init.platforms.forEach((platform) => {
12
+ Object.keys(platform.schemas).forEach((key) => {
13
+ if (! platform.schemas[key]) { return; }
14
+ Schemas.validator.addPlatformSchema(platform.schemas[key], `${platform.id}/${key}`);
15
+ });
16
+ });
17
+
18
+ // called when registered with the middleware function, define the type of validation
19
+ // that will be called when the middleware eventually does.
20
+ export default function validate(type: string, sockethubId: string) {
21
+ const sessionLog = debug(`sockethub:validate:${sockethubId}`);
22
+ return (msg: ActivityStream, done: Function) => {
23
+ sessionLog('applying schema validation for ' + type);
24
+ if (type === 'activity-object') {
25
+ const err = Schemas.validator.validateActivityObject(msg);
26
+ err ? done(new Error(err)) : done(msg);
27
+ } else if (! init.platforms.has(msg.context)) {
28
+ return done(
29
+ new Error(`platform context ${msg.context} not registered with this sockethub instance.`)
30
+ );
31
+ } else if (type === 'credentials') {
32
+ const err = Schemas.validator.validateCredentials(msg);
33
+ err ? done(new Error(err)) : done(msg);
34
+ } else {
35
+ const err = Schemas.validator.validateActivityStream(msg);
36
+ if (err) {
37
+ done(new Error(err));
38
+ } else {
39
+ const platformMeta = init.platforms.get(msg.context);
40
+ if (!platformMeta.types.includes(msg.type)) {
41
+ return done(new Error(`platform type ${msg.type} not supported by ${msg.context} ` +
42
+ `platform. (types: ${platformMeta.types.join(', ')})`));
43
+ } else {
44
+ done(msg);
45
+ }
46
+ }
47
+ }
48
+ };
49
+ };
@@ -0,0 +1,148 @@
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 CHANGED
@@ -1,57 +1,52 @@
1
- /**
2
- * a very simple middleware handler
3
- *
4
- * When initialized, provide a function which will be called if there were any failures during
5
- * the execution of functions along the chain.
6
- *
7
- * Use middleware.chain, passing in a list of functions to call in order. It then returns
8
- * a function which accepts the message from the input. That function then will
9
- * call each of the originally passed in functions, in order, with a `next` callback as
10
- * the first parameter, and any number of originating parameters.
11
- *
12
- * As one middleware function is done, they call `next` with the first argument `true`
13
- * (succeeded, continue) any parameters to pass along.
14
- *
15
- * If any of the middleware function calls the `next` handler with `false` as the first param, the
16
- * execution of the function chain is halted, and the failure callback is called. Again, any
17
- * number of params passed after the `false` will be passed to the failure callback.
18
- *
19
- */
20
- function Middleware(errorHandler: Function) {
21
- this.errorHandler = errorHandler;
1
+ import { debug } from 'debug';
2
+
3
+ export default function middleware(name: string): MiddlewareChain {
4
+ return new MiddlewareChain(name);
22
5
  }
23
6
 
24
- Middleware.prototype.chain = function (...funcs) {
25
- let self = this;
26
- return (...params) => {
27
- let count = 0;
28
- if (typeof funcs[count] !== 'function') {
29
- throw new Error('middleware function can only take other functions as arguments.');
30
- }
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;
31
12
 
32
- function _callFunc(pos: number) {
33
- if (pos + 1 === funcs.length) {
34
- // last call, don't wait for next callback
35
- funcs[pos].apply(this, params);
36
- return;
37
- }
38
-
39
- function _nextFunc(status, ..._params) {
40
- if ((typeof status === 'boolean') && (! status)) {
41
- // failed, abort.
42
- self.errorHandler(..._params);
43
- } else if ((status) && (_params.length > -1)) {
44
- // re-assign/update the payload object
45
- params = _params;
46
- }
47
- count = count + 1;
48
- setTimeout(_callFunc.bind(this, count), 0);
49
- }
13
+ constructor(name: string) {
14
+ this.name = name;
15
+ this.logger = debug(`sockethub:middleware:${name}`);
16
+ }
50
17
 
51
- funcs[pos].apply(this, [_nextFunc].concat(params));
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);
52
29
  }
53
- _callFunc(count);
54
- };
55
- };
30
+ return this;
31
+ }
56
32
 
57
- export default Middleware;
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
+ }