@weave-js/core 0.12.1 → 0.14.0

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.
@@ -1,6 +1,6 @@
1
1
  class ExtendableError extends Error {
2
- constructor (message = '') {
3
- super(message);
2
+ constructor (message = '', options) {
3
+ super(message, options);
4
4
 
5
5
  Object.defineProperty(this, 'name', {
6
6
  configurable: true,
@@ -21,6 +21,8 @@ const { WeaveMaxCallLevelError, WeaveError } = require('../errors');
21
21
  * @returns {Context} Context
22
22
  */
23
23
  exports.createContext = (runtime) => {
24
+ const spanStack = [];
25
+
24
26
  /** @type {Context} */
25
27
  const context = {
26
28
  id: null,
@@ -95,17 +97,33 @@ exports.createContext = (runtime) => {
95
97
  sampled: this.tracing
96
98
  }, options);
97
99
 
100
+ let span;
101
+
98
102
  if (this.span) {
99
- this.span = this.span.startChildSpan(name, options);
103
+ span = this.span.startChildSpan(name, options);
100
104
  } else {
101
- this.span = runtime.tracer.startSpan(name, options);
105
+ span = runtime.tracer.startSpan(name, options);
102
106
  }
107
+ spanStack.push(span);
108
+ this.span = span;
109
+
103
110
  return this.span;
104
111
  },
105
- finishSpan () {
106
- if (this.span) {
107
- this.span.finish();
108
- return this.span;
112
+ finishSpan (span, time) {
113
+ if (span && !span.isActive()) {
114
+ return;
115
+ }
116
+
117
+ span.finish(time);
118
+
119
+ const idx = spanStack.findIndex((s) => s === span);
120
+
121
+ if (idx !== -1) {
122
+ spanStack.splice(idx, 1);
123
+ this.span = spanStack[spanStack.length - 1];
124
+ } else {
125
+ /* istanbul ignore next */
126
+ this.service.log.warn('This span is not assigned to this context', span);
109
127
  }
110
128
  },
111
129
  /**
@@ -132,6 +150,8 @@ exports.createContext = (runtime) => {
132
150
  }
133
151
  };
134
152
 
153
+
154
+
135
155
  // Generate context Id
136
156
  if (!context.id) {
137
157
  // Use UUID factory from broker options
@@ -82,6 +82,7 @@ exports.getDefaultOptions = () => {
82
82
  collectors: [],
83
83
  actions: {
84
84
  data: false,
85
+ response: false,
85
86
  tags: {}
86
87
  },
87
88
  events: {
package/lib/errors.js CHANGED
@@ -4,25 +4,30 @@
4
4
  * Copyright 2021 Fachwerk
5
5
  */
6
6
 
7
+ const { defaultsDeep } = require('@weave-js/utils');
7
8
  const { ExtendableError } = require('./ExtendableError');
8
9
 
9
10
  class WeaveError extends ExtendableError {
10
- constructor (message, code, type, data) {
11
- super(message);
11
+ constructor (message, options) {
12
+ options = defaultsDeep(
13
+ options,
14
+ {
15
+ code: 'WEAVE_ERROR',
16
+ retryable: false
17
+ }
18
+ );
19
+
20
+ super(message, options);
12
21
  this.name = this.constructor.name;
13
- this.code = code || 500;
14
- this.type = type;
15
- this.data = data;
22
+ this.code = options.code || 'WEAVE_ERROR';
23
+ this.data = options.data;
16
24
  this.retryable = false;
17
25
  }
18
26
  }
19
27
 
20
28
  class WeaveRetryableError extends WeaveError {
21
- constructor (message, code, type, data) {
22
- super(message);
23
- this.code = code || 500;
24
- this.type = type;
25
- this.data = data;
29
+ constructor (message, options = { code: 'WEAVE_RETRYABLE_ERROR', retryable: true }) {
30
+ super(message, options);
26
31
  this.retryable = true;
27
32
  }
28
33
  }
@@ -39,11 +44,11 @@ class WeaveServiceNotFoundError extends WeaveRetryableError {
39
44
  message = 'Service not found.';
40
45
  }
41
46
 
42
- super(message, 404, 'WEAVE_SERVICE_NOT_FOUND_ERROR', data);
47
+ super(message, { code: 'WEAVE_SERVICE_NOT_FOUND_ERROR', data });
43
48
  }
44
49
  }
45
50
 
46
- class WeaveServiceNotAvailableError extends WeaveRetryableError {
51
+ class WeaveServiceNotAvailableError extends WeaveRetryableError { // 503
47
52
  constructor (data = {}) {
48
53
  let message;
49
54
  if (data.nodeId) {
@@ -54,67 +59,69 @@ class WeaveServiceNotAvailableError extends WeaveRetryableError {
54
59
  message = 'Service not available.';
55
60
  }
56
61
 
57
- super(message, 503, 'WEAVE_SERVICE_NOT_AVAILABLE_ERROR', data);
62
+ super(message, { code: 'WEAVE_SERVICE_NOT_AVAILABLE_ERROR', data });
58
63
  }
59
64
  }
60
65
 
61
- class WeaveRequestTimeoutError extends WeaveRetryableError {
66
+ class WeaveRequestTimeoutError extends WeaveRetryableError { // 504
62
67
  constructor (actionName, nodeId, timeout) {
63
- const message = `Action ${actionName} timed out node ${nodeId || '<local>'}.`;
64
- super(message, 504, 'WEAVE_REQUEST_TIMEOUT_ERROR', {
68
+ const data = {
65
69
  actionName,
66
70
  nodeId,
67
71
  timeout
68
- });
69
- this.retryable = true;
72
+ };
73
+
74
+ const message = `Action ${actionName} timed out node ${nodeId || '<local>'}.`;
75
+ super(message, { code: 'WEAVE_REQUEST_TIMEOUT_ERROR', data });
70
76
  }
71
77
  }
72
78
 
73
- class WeaveParameterValidationError extends WeaveError {
79
+ class WeaveParameterValidationError extends WeaveError { // 422
74
80
  constructor (message, data) {
75
- super(message, 422, 'WEAVE_PARAMETER_VALIDATION_ERROR', data);
81
+ super(message, { code: 'WEAVE_PARAMETER_VALIDATION_ERROR', data });
76
82
  }
77
83
  }
78
84
 
79
85
  class WeaveBrokerOptionsError extends WeaveError {
80
86
  constructor (message, data) {
81
- super(message, 500, 'WEAVE_BROKER_OPTIONS_ERROR', data);
87
+ super(
88
+ message,
89
+ { code: 'WEAVE_BROKER_OPTIONS_ERROR', data }
90
+ );
82
91
  }
83
92
  }
84
93
 
85
- class WeaveQueueSizeExceededError extends WeaveError {
94
+ class WeaveQueueSizeExceededError extends WeaveError { // 429
86
95
  constructor (data) {
87
- super('Queue size limit was exceeded. Request rejected.', 429, 'WEAVE_QUEUE_SIZE_EXCEEDED_ERROR', data);
96
+ super(
97
+ 'Queue size limit was exceeded. Request rejected.',
98
+ { code: 'WEAVE_QUEUE_SIZE_EXCEEDED_ERROR', data }
99
+ );
88
100
  }
89
101
  }
90
102
 
91
103
  class WeaveMaxCallLevelError extends WeaveError {
92
104
  constructor (data) {
93
- super(`Request level has reached the limit (${data.maxCallLevel}) on node "${data.nodeId}".`, 500, 'WEAVE_MAX_CALL_LEVEL_ERROR', data);
105
+ super(
106
+ `Request level has reached the limit ${data.maxCallLevel} on node "${data.nodeId}".`,
107
+ { code: 'WEAVE_MAX_CALL_LEVEL_ERROR', data }
108
+ );
94
109
  }
95
110
  }
96
111
 
97
112
  class WeaveGracefulStopTimeoutError extends WeaveError {
98
113
  constructor (service) {
99
- super(`Unable to stop service "${service.name}"`, 500, 'GRACEFUL_STOP_TIMEOUT', {
114
+ const data = {
100
115
  name: service.name,
101
116
  version: service.version
102
- });
103
- }
104
- }
105
-
106
- const restoreError = error => {
107
- const ErrorClass = module.exports[error.name];
117
+ };
108
118
 
109
- if (ErrorClass) {
110
- switch (error.name) {
111
- case 'WeaveError':
112
- return new ErrorClass(error.message, error.code, error.type, error.data);
113
- // case 'WeaveParameterValidationError':
114
- // return new ErrorClass(error.message, error.data)
115
- }
119
+ super(
120
+ `Unable to stop service "${service.name}"`,
121
+ { code: 'WEAVE_GRACEFUL_STOP_TIMEOUT', data }
122
+ );
116
123
  }
117
- };
124
+ }
118
125
 
119
126
  module.exports = {
120
127
  WeaveBrokerOptionsError,
@@ -126,6 +133,5 @@ module.exports = {
126
133
  WeaveRetryableError,
127
134
  WeaveServiceNotAvailableError,
128
135
  WeaveServiceNotFoundError,
129
- WeaveGracefulStopTimeoutError,
130
- restoreError
136
+ WeaveGracefulStopTimeoutError
131
137
  };
@@ -12,7 +12,7 @@ module.exports = (runtime) => {
12
12
  return handler(context, serviceInjections)
13
13
  .catch((error) => {
14
14
  if (!(error instanceof Error)) {
15
- error = new WeaveError(error, 500);
15
+ error = new WeaveError(error);
16
16
  }
17
17
 
18
18
  if (runtime.nodeId !== context.nodeId) {
@@ -36,7 +36,7 @@ module.exports = (runtime) => {
36
36
  return handler(context, serviceInjections)
37
37
  .catch((error) => {
38
38
  if (!(error instanceof Error)) {
39
- error = new WeaveError(error, 500);
39
+ error = new WeaveError(error.message, { cause: error });
40
40
  }
41
41
 
42
42
  if (runtime.nodeId !== context.nodeId) {
@@ -4,22 +4,56 @@
4
4
  * Copyright 2021 Fachwerk
5
5
  */
6
6
 
7
- const { isPlainObject } = require('@weave-js/utils');
7
+ const { isPlainObject, isFunction, isObject, dotGet } = require('@weave-js/utils');
8
8
  const { buildActionTags, buildEventTags } = require('./tags');
9
9
 
10
- const wrapTracingLocalActionMiddleware = function (handler) {
10
+ const wrapTracingLocalActionMiddleware = function (handler, action) {
11
11
  const broker = this;
12
12
  const tracingOptions = broker.options.tracing || {};
13
+ const actionTracingOptions = action.tracing || {};
13
14
 
14
15
  if (tracingOptions.enabled) {
15
16
  return function metricsLocalMiddleware (context, serviceInjections) {
16
17
  const tags = buildActionTags(context, tracingOptions);
17
18
 
18
- if (tracingOptions) {
19
+ if (tracingOptions.actions.data) {
19
20
  tags.data = context.data !== null && isPlainObject(context.data) ? Object.assign({}, context.data) : context.data;
20
21
  }
21
22
 
22
- const spanName = `action "${context.action.name}"`;
23
+ const globalActionTags = tracingOptions.actions.tags;
24
+ let actionTags;
25
+ // local action tags take precedence
26
+ if (isFunction(actionTracingOptions.tags)) {
27
+ actionTags = actionTracingOptions.tags;
28
+ } else if (!actionTracingOptions.tags && isFunction(globalActionTags)) {
29
+ actionTags = globalActionTags;
30
+ } else {
31
+ // By default all params are captured. This can be overridden globally and locally
32
+ actionTags = { ...{ data: true }, ...globalActionTags, ...actionTracingOptions.tags };
33
+ }
34
+
35
+ if (isObject(actionTracingOptions.tags)) {
36
+ if (Array.isArray(actionTags.data)) {
37
+ tags.data = actionTags.data.reduce((acc, current) => {
38
+ acc[current] = dotGet(context.data, current);
39
+ return acc;
40
+ }, {});
41
+ }
42
+ }
43
+
44
+ // Span name
45
+ let spanName = `action "${context.action.name}"`;
46
+
47
+ if (actionTracingOptions.spanName) {
48
+ switch (typeof actionTracingOptions.spanName) {
49
+ case 'string':
50
+ spanName = actionTracingOptions.spanName;
51
+ break;
52
+ case 'function':
53
+ spanName = actionTracingOptions.spanName.call(context.service, context);
54
+ break;
55
+ }
56
+ }
23
57
 
24
58
  const span = context.startSpan(spanName, {
25
59
  id: context.id,
@@ -39,18 +73,17 @@ const wrapTracingLocalActionMiddleware = function (handler) {
39
73
  isCachedResult: context.isCachedResult
40
74
  };
41
75
 
42
- if (tracingOptions) {
76
+ if (tracingOptions.actions.response) {
43
77
  tags.response = result !== null && isPlainObject(result) ? Object.assign({}, result) : result;
44
78
  }
45
79
 
46
80
  span.addTags(tags);
47
- span.finish();
81
+ context.finishSpan(span);
48
82
  return result;
49
83
  })
50
84
  .catch(error => {
51
- span
52
- .setError(error)
53
- .finish();
85
+ span.setError(error);
86
+ context.finishSpan(span);
54
87
  return Promise.reject(error);
55
88
  });
56
89
  };
@@ -81,13 +114,12 @@ const wrapTracingLocalEventMiddleware = function (handler, event) {
81
114
 
82
115
  return handler(context)
83
116
  .then(result => {
84
- span.finish();
117
+ context.finishSpan(span);
85
118
  return result;
86
119
  })
87
120
  .catch(error => {
88
- span
89
- .setError(error)
90
- .finish();
121
+ span.setError(error);
122
+ context.finishSpan(span);
91
123
  return Promise.reject(error);
92
124
  });
93
125
  };
@@ -93,7 +93,12 @@ exports.initServiceManager = (runtime) => {
93
93
  }
94
94
 
95
95
  if (timeout && (Date.now() - startTimestamp) > timeout) {
96
- return reject(new WeaveError('The waiting of the services is interrupted due to a timeout.', 500, 'WAIT_FOR_SERVICE', { services: serviceNames }));
96
+ return reject(new WeaveError('The waiting of the services is interrupted due to a timeout.', {
97
+ code: 'WAIT_FOR_SERVICE',
98
+ data: {
99
+ services: serviceNames
100
+ }
101
+ }));
97
102
  }
98
103
 
99
104
  options.waitForServiceInterval = setTimeout(serviceCheck, interval);
@@ -42,7 +42,7 @@ exports.createSpan = (tracer, name, options) => {
42
42
  span.finish = (time) => {
43
43
  span.finishTime = time || hrTime();
44
44
  span.duration = span.finishTime - span.startTime;
45
- tracer.log.debug('Span finished');
45
+ tracer.log.debug(`Span "${span.id}" finished`);
46
46
  tracer.invokeCollectorMethod('finishedSpan', [span]);
47
47
 
48
48
  return span;
@@ -6,7 +6,6 @@ const errorPayloadFactory =
6
6
  message: error.message,
7
7
  nodeId: error.nodeId || runtime.nodeId,
8
8
  code: error.code,
9
- type: error.type,
10
9
  stack: error.stack,
11
10
  data: error.data
12
11
  };
@@ -5,10 +5,11 @@
5
5
  */
6
6
 
7
7
  const { InboundTransformStream } = require('./InboundTransformStream');
8
- const { WeaveError, restoreError } = require('../errors');
8
+ const { WeaveError } = require('../errors');
9
9
  const { createContext } = require('../broker/context');
10
10
  const { createMessage } = require('./createMessage');
11
11
  const MessageTypes = require('./messageTypes');
12
+ const { restoreError } = require('../utils/restoreError');
12
13
 
13
14
  /**
14
15
  * @typedef {import('../types').Transport} Transport
@@ -291,23 +292,10 @@ module.exports = (runtime, transport) => {
291
292
 
292
293
  // Response is an error
293
294
  if (!payload.success) {
294
- let error = restoreError(payload.error);
295
-
296
- if (!error) {
297
- error = new Error(payload.error.message);
298
- error.name = payload.error.name;
299
- error.code = payload.error.code;
300
- error.type = payload.error.type;
301
- error.data = payload.error.data;
302
- }
295
+ const error = restoreError(payload.error);
303
296
 
304
- error.retryable = payload.error.retryable;
305
297
  error.nodeId = error.nodeId || payload.sender;
306
298
 
307
- if (payload.error.stack) {
308
- error.stack = payload.error.stack;
309
- }
310
-
311
299
  request.reject(error);
312
300
  }
313
301
 
@@ -398,6 +386,7 @@ module.exports = (runtime, transport) => {
398
386
  const onHeartbeat = (payload) => {
399
387
  transport.log.verbose(`Heartbeat from ${payload.sender}`);
400
388
  const node = registry.nodeCollection.get(payload.sender);
389
+
401
390
  // if node is unknown then request a node info message.
402
391
  if (node) {
403
392
  if (!node.isAvailable) {
@@ -510,7 +499,11 @@ module.exports = (runtime, transport) => {
510
499
 
511
500
  return true;
512
501
  } catch (error) {
513
- transport.log.error(error, data);
502
+ transport.log.error(error, type, data);
503
+
504
+ runtime.eventBus.broadcastLocal('$transport.error', {
505
+ error
506
+ });
514
507
  }
515
508
  return false;
516
509
  };
@@ -1,12 +1,64 @@
1
1
  const errors = require('../errors');
2
2
 
3
- exports.restoreError = (error) => {
4
- const ErrorClass = errors[error.name];
3
+ const restoreError = (errorPayload) => {
4
+ const ErrorClass = errors[errorPayload.name];
5
+ let error;
5
6
 
6
7
  if (ErrorClass) {
7
- switch (error.name) {
8
+ switch (errorPayload.name) {
8
9
  case 'WeaveError':
9
- return new ErrorClass(error.message, error.code, error.type, error.data);
10
+ case 'WeaveRetryableError': {
11
+ const { message, ...options } = errorPayload;
12
+ error = new ErrorClass(message, options);
13
+ break;
14
+ }
15
+
16
+ case 'WeaveParameterValidationError':
17
+ case 'WeaveBrokerOptionsError': {
18
+ const { message, data } = errorPayload;
19
+ error = new ErrorClass(message, data);
20
+ break;
21
+ }
22
+ case 'WeaveServiceNotFoundError':
23
+ case 'WeaveServiceNotAvailableError':
24
+ case 'WeaveQueueSizeExceededError':
25
+ case 'WeaveMaxCallLevelError': {
26
+ const { data } = errorPayload;
27
+ error = new ErrorClass(data);
28
+ break;
29
+ }
30
+ case 'WeaveRequestTimeoutError': {
31
+ const { data } = errorPayload;
32
+ error = new ErrorClass(data.actionName, data.nodeId, data.timeout);
33
+ break;
34
+ }
35
+ case 'WeaveGracefulStopTimeoutError': {
36
+ const { data } = errorPayload;
37
+ error = new ErrorClass(data.service);
38
+ break;
39
+ }
40
+ }
41
+ } else {
42
+ error = new Error(errorPayload.message);
43
+
44
+ error.name = errorPayload.name;
45
+
46
+ error.nodeId = errorPayload.nodeId;
47
+
48
+ if (errorPayload.code) {
49
+ error.code = errorPayload.code;
50
+ }
51
+
52
+ if (errorPayload.data) {
53
+ error.data = errorPayload.data;
10
54
  }
11
55
  }
56
+
57
+ if (errorPayload.stack) {
58
+ error.stack = errorPayload.stack;
59
+ }
60
+
61
+ return error;
12
62
  };
63
+
64
+ module.exports = { restoreError };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weave-js/core",
3
- "version": "0.12.1",
3
+ "version": "0.14.0",
4
4
  "description": "The core package of weave",
5
5
  "keywords": [
6
6
  "Weave",
@@ -40,14 +40,14 @@
40
40
  "license": "MIT",
41
41
  "dependencies": {
42
42
  "@weave-js/errors": "^0.9.1",
43
- "@weave-js/utils": "^0.11.1",
44
- "@weave-js/validator": "^0.12.0",
45
- "eventemitter2": "^6.4.5",
46
- "glob": "^7.2.0"
43
+ "@weave-js/utils": "^0.13.0",
44
+ "@weave-js/validator": "^0.14.0",
45
+ "eventemitter2": "^6.4.9",
46
+ "glob": "^10.2.2"
47
47
  },
48
48
  "directories": {
49
49
  "lib": "lib",
50
50
  "test": "test"
51
51
  },
52
- "gitHead": "3f9662a98caa08d32ba5a181cc5871318aaece23"
52
+ "gitHead": "c2f63481b2a72ae03a35682c9a6d12800f22dbf2"
53
53
  }