@weave-js/core 0.9.4 → 0.11.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 (98) hide show
  1. package/lib/ExtendableError.js +33 -0
  2. package/lib/broker/context.js +25 -13
  3. package/lib/broker/{default-options.js → defaultOptions.js} +25 -6
  4. package/lib/broker/index.js +224 -194
  5. package/lib/buildRuntime.js +70 -0
  6. package/lib/cache/adapters/base.js +143 -0
  7. package/lib/cache/adapters/inMemory.js +154 -0
  8. package/lib/cache/adapters/inMemoryLru.js +140 -0
  9. package/lib/cache/adapters/index.js +46 -0
  10. package/lib/cache/base.js +49 -44
  11. package/lib/cache/lock.js +21 -4
  12. package/lib/constants.js +8 -5
  13. package/lib/errorHandler.js +19 -0
  14. package/lib/errors.js +17 -10
  15. package/lib/helper/{define-action.js → defineAction.js} +3 -1
  16. package/lib/helper/{define-broker-options.js → defineBrokerOptions.js} +2 -0
  17. package/lib/helper/{define-service.js → defineService.js} +4 -1
  18. package/lib/index.js +17 -92
  19. package/lib/logger/format/asHumanReadable.js +4 -3
  20. package/lib/logger/index.js +7 -1
  21. package/lib/logger/levels.js +8 -7
  22. package/lib/logger/tools.js +4 -0
  23. package/lib/logger/utils/format.js +18 -4
  24. package/lib/metrics/constants.js +8 -6
  25. package/lib/metrics/types/gauge.js +1 -1
  26. package/lib/middlewares/action-hooks/index.js +62 -14
  27. package/lib/middlewares/bulkhead/index.js +5 -4
  28. package/lib/middlewares/cache/index.js +106 -0
  29. package/lib/middlewares/circuit-breaker/index.js +41 -16
  30. package/lib/middlewares/context-tracker/index.js +3 -3
  31. package/lib/middlewares/error-handler/index.js +3 -3
  32. package/lib/middlewares/index.js +2 -1
  33. package/lib/middlewares/metrics/getMiddlewareWrapper.js +2 -2
  34. package/lib/middlewares/metrics/index.js +2 -2
  35. package/lib/middlewares/retry/index.js +11 -10
  36. package/lib/middlewares/timeout/index.js +5 -5
  37. package/lib/middlewares/tracing/index.js +27 -14
  38. package/lib/middlewares/tracing/tags.js +43 -0
  39. package/lib/middlewares/validator/index.js +6 -6
  40. package/lib/registry/{action-endpoint.js → actionEndpoint.js} +0 -0
  41. package/lib/registry/collections/{action-collection.js → actionCollection.js} +2 -2
  42. package/lib/registry/collections/{endpoint-collection.js → endpointCollection.js} +19 -3
  43. package/lib/registry/collections/{event-collection.js → eventCollection.js} +15 -9
  44. package/lib/registry/collections/{node-collection.js → nodeCollection.js} +1 -1
  45. package/lib/registry/collections/{service-collection.js → serviceCollection.js} +2 -2
  46. package/lib/registry/{event-endpoint.js → eventEndpoint.js} +1 -1
  47. package/lib/registry/node.js +1 -1
  48. package/lib/registry/registry.js +19 -38
  49. package/lib/registry/service/service.js +2 -2
  50. package/lib/registry/{service-item.js → serviceItem.js} +1 -1
  51. package/lib/runtime/{init-action-invoker.js → initActionInvoker.js} +3 -3
  52. package/lib/runtime/initCache.js +19 -0
  53. package/lib/runtime/{init-context-factory.js → initContextFactory.js} +2 -2
  54. package/lib/runtime/{init-eventbus.js → initEventbus.js} +11 -10
  55. package/lib/runtime/{init-logger.js → initLogger.js} +0 -0
  56. package/lib/runtime/{init-metrics.js → initMetrics.js} +11 -10
  57. package/lib/runtime/{init-middleware-manager.js → initMiddlewareManager.js} +1 -1
  58. package/lib/runtime/{init-registry.js → initRegistry.js} +1 -1
  59. package/lib/runtime/{init-service-manager.js → initServiceManager.js} +0 -0
  60. package/lib/runtime/{init-tracing.js → initTracing.js} +2 -1
  61. package/lib/runtime/{init-transport.js → initTransport.js} +0 -0
  62. package/lib/runtime/{init-uuid-factory.js → initUuidFactory.js} +0 -0
  63. package/lib/runtime/{init-validator.js → initValidator.js} +1 -1
  64. package/lib/tracing/collectors/base.js +11 -19
  65. package/lib/tracing/collectors/event.js +2 -2
  66. package/lib/tracing/collectors/index.js +2 -2
  67. package/lib/tracing/span.js +8 -6
  68. package/lib/transport/InboundTransformStream.js +75 -0
  69. package/lib/transport/adapters/{adapter-base.js → adapteBase.js} +16 -1
  70. package/lib/transport/adapters/adapters.js +1 -1
  71. package/lib/transport/adapters/dummy/index.js +3 -3
  72. package/lib/transport/adapters/index.js +1 -1
  73. package/lib/transport/adapters/tcp/discovery/index.js +45 -31
  74. package/lib/transport/adapters/tcp/index.js +20 -13
  75. package/lib/transport/adapters/tcp/tcpReader.js +1 -0
  76. package/lib/transport/adapters/tcp/tcpWriteStream.js +1 -1
  77. package/lib/transport/adapters/tcp/tcpWriter.js +2 -2
  78. package/lib/transport/createMessage.js +11 -0
  79. package/lib/transport/createTransport.js +215 -172
  80. package/lib/transport/errorPayloadFactory.js +18 -0
  81. package/lib/transport/{message-handlers.js → messageHandlers.js} +92 -23
  82. package/lib/transport/messageTypes.js +23 -0
  83. package/lib/types.js +86 -75
  84. package/lib/utils/index.js +1 -1
  85. package/lib/utils/options.js +4 -2
  86. package/lib/utils/{restore-error.js → restoreError.js} +1 -1
  87. package/package.json +6 -12
  88. package/CHANGELOG.md +0 -1317
  89. package/lib/cache/index.js +0 -48
  90. package/lib/cache/memory.js +0 -91
  91. package/lib/middlewares/tracing/buildTags.js +0 -17
  92. package/lib/new-errors.js +0 -25
  93. package/lib/runtime/init-cache.js +0 -10
  94. package/lib/tracing/collectors/console.js +0 -34
  95. package/lib/tracing/index.js +0 -3
  96. package/lib/tracing/tracer.js +0 -64
  97. package/lib/transport/message-types.js +0 -59
  98. package/lib/transport/message.js +0 -16
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @typedef {import('./types.js').BrokerOptions} BrokerOptions
3
+ * @typedef {import('./types.js').Runtime} Runtime
4
+ * @typedef {import('./types.js').Broker} Broker
5
+ */
6
+
7
+ const { initLogger } = require('./runtime/initLogger')
8
+ const { initMiddlewareHandler } = require('./runtime/initMiddlewareManager')
9
+ const { initRegistry } = require('./runtime/initRegistry')
10
+ const { initContextFactory } = require('./runtime/initContextFactory')
11
+ const { initEventbus } = require('./runtime/initEventbus')
12
+ const { initValidator } = require('./runtime/initValidator')
13
+ const { initTransport } = require('./runtime/initTransport')
14
+ const { initCache } = require('./runtime/initCache')
15
+ const { initActionInvoker } = require('./runtime/initActionInvoker')
16
+ const { initServiceManager } = require('./runtime/initServiceManager')
17
+ const { initMetrics } = require('./runtime/initMetrics')
18
+ const { initTracer } = require('./runtime/initTracing')
19
+ const { initUUIDFactory } = require('./runtime/initUuidFactory')
20
+ const { errorHandler, fatalErrorHandler } = require('./errorHandler')
21
+ const { uuid } = require('@weave-js/utils')
22
+ const { version } = require('../package.json')
23
+ const EventEmitter = require('eventemitter2')
24
+
25
+ /**
26
+ * Build runtime object
27
+ * @param {BrokerOptions} options Broker options
28
+ * @return {Runtime} Runtime
29
+ */
30
+ exports.initRuntime = (options) => {
31
+ /**
32
+ * Event bus
33
+ * @returns {EventEmitter} Service object.
34
+ */
35
+ const bus = new EventEmitter({
36
+ wildcard: true,
37
+ maxListeners: 1000
38
+ })
39
+
40
+ // Create base runtime object
41
+ const runtime = {
42
+ nodeId: options.nodeId,
43
+ version,
44
+ options,
45
+ bus,
46
+ state: {
47
+ instanceId: uuid(),
48
+ isStarted: false
49
+ },
50
+ handleError: (error) => errorHandler(runtime, error),
51
+ fatalError: (message, error, killProcess) => fatalErrorHandler(runtime, message, error, killProcess)
52
+ }
53
+
54
+ // Init modules
55
+ initLogger(runtime)
56
+ initUUIDFactory(runtime)
57
+ initMiddlewareHandler(runtime)
58
+ initRegistry(runtime)
59
+ initContextFactory(runtime)
60
+ initEventbus(runtime)
61
+ initValidator(runtime)
62
+ initTransport(runtime)
63
+ initCache(runtime)
64
+ initActionInvoker(runtime)
65
+ initServiceManager(runtime)
66
+ initMetrics(runtime)
67
+ initTracer(runtime)
68
+
69
+ return runtime
70
+ }
@@ -0,0 +1,143 @@
1
+ /*
2
+ * Author: Kevin Ries (kevin@fachw3rk.de)
3
+ * -----
4
+ * Copyright 2021 Fachwerk
5
+ */
6
+ const crypto = require('crypto')
7
+ const { isObject, dotGet, isString } = require('@weave-js/utils')
8
+ const Constants = require('../../metrics/constants')
9
+ const { WeaveError } = require('../../errors')
10
+
11
+ /**
12
+ * Get property from data or metadata object.
13
+ * @param {any} data data object
14
+ * @param {object} metadata metadata object
15
+ * @param {string} key key
16
+ * @returns {any} Result
17
+ */
18
+ function getPropertyFromDataOrMetadata (data, metadata, key) {
19
+ // if a key starts with ":", the property is picked from metadata
20
+ if (key.startsWith(':')) {
21
+ // remove ':' from key.
22
+ key = key.replace(':', '')
23
+ return dotGet(metadata, key)
24
+ }
25
+ return dotGet(data, key)
26
+ }
27
+
28
+ function getCacheKeyByObject (val) {
29
+ if (Array.isArray(val)) {
30
+ return val.map(object => getCacheKeyByObject(object)).join('/')
31
+ } else if (isObject(val)) {
32
+ return Object.keys(val).map(key => {
33
+ return [key, getCacheKeyByObject(val[key])].join('/')
34
+ }).join('/')
35
+ } else if (val !== null) {
36
+ return val.toString()
37
+ } else {
38
+ return 'null'
39
+ }
40
+ }
41
+
42
+ function generateHash (key) {
43
+ return crypto
44
+ .createHash('sha1')
45
+ .update(key)
46
+ .digest('base64')
47
+ }
48
+
49
+ function registerCacheMetrics (metrics) {
50
+ metrics.register({ type: 'counter', name: Constants.CACHE_GET_TOTAL })
51
+ metrics.register({ type: 'counter', name: Constants.CACHE_SET_TOTAL })
52
+ metrics.register({ type: 'counter', name: Constants.CACHE_FOUND_TOTAL })
53
+ metrics.register({ type: 'counter', name: Constants.CACHE_EXPIRED_TOTAL })
54
+ metrics.register({ type: 'counter', name: Constants.CACHE_DELETED_TOTAL })
55
+ metrics.register({ type: 'counter', name: Constants.CACHE_CLEANED_TOTAL })
56
+ }
57
+
58
+ const createCacheBase = (name, runtime, adapterOptions, options) => {
59
+ if (!isString(name)) {
60
+ throw new WeaveError('Name must be a string.')
61
+ }
62
+
63
+ const cache = {
64
+ name,
65
+ isConnected: false,
66
+ runtime,
67
+ options: Object.assign({
68
+ ttl: null
69
+ }, options),
70
+ init () {
71
+ // register metrics
72
+ if (runtime.metrics) {
73
+ this.metrics = runtime.metrics
74
+ registerCacheMetrics(runtime.metrics)
75
+ }
76
+ },
77
+ log: runtime.createLogger('CACHER'),
78
+ set (/* hashKey, result, ttl */) {
79
+ /* istanbul ignore next */
80
+ runtime.handleError(new Error('Method not implemented.'))
81
+ },
82
+ get (/* hashKey */) {
83
+ /* istanbul ignore next */
84
+ runtime.handleError(new Error('Method not implemented.'))
85
+ },
86
+ remove () {
87
+ /* istanbul ignore next */
88
+ runtime.handleError(new Error('Method not implemented.'))
89
+ },
90
+ clear () {
91
+ /* istanbul ignore next */
92
+ runtime.handleError(new Error('Method not implemented.'))
93
+ },
94
+ stop () {
95
+ /* istanbul ignore next */
96
+ return Promise.resolve()
97
+ },
98
+ getCachingKey (actionName, data, metadata, keys) {
99
+ if (data || metadata) {
100
+ const prefix = actionName + ':'
101
+
102
+ if (keys) {
103
+ // fast path for single keys
104
+ if (keys.length === 1) {
105
+ const value = getPropertyFromDataOrMetadata(data, metadata, keys[0])
106
+ const key = getCacheKeyByObject(value)
107
+ return prefix + (isObject(value) ? key : value)
108
+ }
109
+
110
+ // Handle data cache keys
111
+ if (keys.length > 0) {
112
+ const res = keys.reduce((pre, property, index) => {
113
+ const value = getPropertyFromDataOrMetadata(data, metadata, property)
114
+ let hash
115
+ if (isObject(value)) {
116
+ const key = getCacheKeyByObject(value)
117
+ hash = generateHash(key)
118
+ } else {
119
+ hash = value
120
+ }
121
+
122
+ return pre + (index > 0 ? '|' : '') + hash
123
+ }, prefix)
124
+ return res
125
+ }
126
+ } else {
127
+ return prefix + generateHash(getCacheKeyByObject(data))
128
+ }
129
+ }
130
+
131
+ return actionName
132
+ }
133
+ }
134
+
135
+ Object.defineProperty(cache, 'adapterOptions', {
136
+ value: adapterOptions,
137
+ writable: false
138
+ })
139
+
140
+ return cache
141
+ }
142
+
143
+ module.exports = { createCacheBase }
@@ -0,0 +1,154 @@
1
+ /*
2
+ * Author: Kevin Ries (kevin@fachw3rk.de)
3
+ * -----
4
+ * Copyright 2021 Fachwerk
5
+ */
6
+
7
+ const { match, defaultsDeep } = require('@weave-js/utils')
8
+ const { createCacheBase } = require('./base')
9
+ const { createLock } = require('../lock')
10
+ const Constants = require('../../metrics/constants')
11
+
12
+ const defaultAdapterOptions = {
13
+ ttlCheckInterval: 3000
14
+ }
15
+
16
+ /**
17
+ * @typedef {Object} InMemoryAdapterOptions
18
+ * @property {number=} ttlCheckInterval TTL check interval
19
+ */
20
+
21
+ /**
22
+ * Create an in-memory cache adapter.
23
+ * @param {InMemoryAdapterOptions} adapterOptions Adapter options
24
+ * @returns {any} CacheFactory
25
+ */
26
+ const createInMemoryCache = (adapterOptions = {}) => (runtime, options = {}) => {
27
+ adapterOptions = defaultsDeep(adapterOptions, defaultAdapterOptions)
28
+ const base = createCacheBase('In-Memory', runtime, adapterOptions, options)
29
+ const storage = new Map()
30
+
31
+ const lock = createLock()
32
+
33
+ const ttlTimerHandle = setInterval(() => {
34
+ checkTtl()
35
+ }, adapterOptions.ttlCheckInterval)
36
+
37
+ ttlTimerHandle.unref()
38
+
39
+ // if a new broker gets connected, we need to clear the cache
40
+ runtime.bus.on('$transport.connected', () => {
41
+ base.log.debug('Transport adapter connected. Cache will be cleared.')
42
+ cache.clear()
43
+ })
44
+
45
+ const checkTtl = () => {
46
+ const now = Date.now()
47
+
48
+ storage.forEach((item, hashKey) => {
49
+ if (item.expire && item.expire < now) {
50
+ cache.log.debug(`Delete ${hashKey}`)
51
+ storage.delete(hashKey)
52
+ }
53
+ })
54
+ }
55
+
56
+ const cache = Object.assign(
57
+ {},
58
+ base,
59
+ {
60
+ init () {
61
+ base.init()
62
+ cache.isConnected = true
63
+ },
64
+ get (cacheKey) {
65
+ base.log.debug(`Get ${cacheKey}`)
66
+
67
+ if (base.metrics) {
68
+ base.metrics.increment(Constants.CACHE_GET_TOTAL)
69
+ }
70
+
71
+ const item = storage.get(cacheKey)
72
+
73
+ if (item) {
74
+ cache.log.debug(`Found ${cacheKey}`)
75
+
76
+ if (base.metrics) {
77
+ base.metrics.increment(Constants.CACHE_FOUND_TOTAL)
78
+ }
79
+
80
+ if (item.expire && item.expire < Date.now()) {
81
+ cache.log.debug(`Delete ${cacheKey}`)
82
+ storage.delete(cacheKey)
83
+ if (base.metrics) {
84
+ base.metrics.increment(Constants.CACHE_EXPIRED_TOTAL)
85
+ }
86
+ return Promise.resolve(null)
87
+ }
88
+
89
+ return Promise.resolve(item.data)
90
+ }
91
+ return Promise.resolve(null)
92
+ },
93
+ set (hashKey, data, ttl) {
94
+ if (base.metrics) {
95
+ base.metrics.increment(Constants.CACHE_SET_TOTAL)
96
+ }
97
+
98
+ // if ttl is not set in action cache settings, use options ttl
99
+ if (ttl == null) {
100
+ ttl = options.ttl
101
+ }
102
+
103
+ storage.set(hashKey, {
104
+ data,
105
+ expire: ttl ? Date.now() + ttl : null
106
+ })
107
+
108
+ base.log.debug(`Set ${hashKey}`)
109
+
110
+ return Promise.resolve(data)
111
+ },
112
+ remove (hashKey) {
113
+ if (base.metrics) {
114
+ base.metrics.increment(Constants.CACHE_DELETED_TOTAL)
115
+ }
116
+ storage.delete(hashKey)
117
+ base.log.debug(`Delete ${hashKey}`)
118
+
119
+ return Promise.resolve()
120
+ },
121
+ clear (pattern = '**') {
122
+ if (base.metrics) {
123
+ base.metrics.increment(Constants.CACHE_DELETED_TOTAL)
124
+ }
125
+ storage.forEach((_, key) => {
126
+ if (match(key, pattern)) {
127
+ base.log.debug(`Delete ${key}`)
128
+ this.remove(key)
129
+ }
130
+ })
131
+ return Promise.resolve()
132
+ },
133
+ lock (key, ttl) {
134
+ return lock.acquire(key, ttl).then(() => {
135
+ return () => lock.release(key)
136
+ })
137
+ },
138
+ tryAcquireLock (key, ttl) {
139
+ if (lock.isLocked(key)) {
140
+ return Promise.reject(new Error('Locked'))
141
+ }
142
+
143
+ return lock.acquire(key, ttl).then(() => {
144
+ return () => lock.release(key)
145
+ })
146
+ },
147
+ async stop () {
148
+ clearInterval(ttlTimerHandle)
149
+ }
150
+ })
151
+ return cache
152
+ }
153
+
154
+ module.exports = { createInMemoryCache }
@@ -0,0 +1,140 @@
1
+ /*
2
+ * Author: Kevin Ries (kevin@fachw3rk.de)
3
+ * -----
4
+ * Copyright 2021 Fachwerk
5
+ */
6
+
7
+ const { match } = require('@weave-js/utils')
8
+ const { createCacheBase } = require('./base')
9
+ const { createLock } = require('../lock')
10
+ const Constants = require('../../metrics/constants')
11
+
12
+ const createInMemoryLruCache = (adapterOptions) => (runtime, options = {}) => {
13
+ const name = 'In-Memory'
14
+ const base = createCacheBase(name, runtime, options)
15
+ const storage = new Map()
16
+
17
+ const lock = createLock()
18
+
19
+ const timer = setInterval(() => {
20
+ checkTtl()
21
+ }, 3000)
22
+
23
+ timer.unref()
24
+
25
+ // if a new broker gets connected, we need to clear the cache
26
+ runtime.bus.on('$transport.connected', () => {
27
+ base.log.debug('Transport adapter connected. Cache will be cleared.')
28
+ cache.clear()
29
+ })
30
+
31
+ const checkTtl = () => {
32
+ const now = Date.now()
33
+
34
+ storage.forEach((item, hashKey) => {
35
+ if (item.expire && item.expire < now) {
36
+ cache.log.debug(`Delete ${hashKey}`)
37
+ storage.delete(hashKey)
38
+ }
39
+ })
40
+ }
41
+
42
+ const cache = Object.assign(
43
+ {},
44
+ base,
45
+ {
46
+ name,
47
+ init () {
48
+ base.init()
49
+ cache.isConnected = true
50
+ },
51
+ get (cacheKey) {
52
+ base.log.debug(`Get ${cacheKey}`)
53
+
54
+ if (base.metrics) {
55
+ base.metrics.increment(Constants.CACHE_GET_TOTAL)
56
+ }
57
+
58
+ const item = storage.get(cacheKey)
59
+
60
+ if (item) {
61
+ cache.log.debug(`Found ${cacheKey}`)
62
+
63
+ if (base.metrics) {
64
+ base.metrics.increment(Constants.CACHE_FOUND_TOTAL)
65
+ }
66
+
67
+ if (item.expire && item.expire < Date.now()) {
68
+ cache.log.debug(`Delete ${cacheKey}`)
69
+ storage.delete(cacheKey)
70
+ if (base.metrics) {
71
+ base.metrics.increment(Constants.CACHE_EXPIRED_TOTAL)
72
+ }
73
+ return Promise.resolve(null)
74
+ }
75
+
76
+ return Promise.resolve(item.data)
77
+ }
78
+ return Promise.resolve(null)
79
+ },
80
+ set (hashKey, data, ttl) {
81
+ if (base.metrics) {
82
+ base.metrics.increment(Constants.CACHE_SET_TOTAL)
83
+ }
84
+
85
+ if (ttl == null) {
86
+ ttl = options.ttl
87
+ }
88
+
89
+ storage.set(hashKey, {
90
+ data,
91
+ expire: ttl ? Date.now() + ttl : null
92
+ })
93
+
94
+ base.log.debug(`Set ${hashKey}`)
95
+
96
+ return Promise.resolve(data)
97
+ },
98
+ remove (hashKey) {
99
+ if (base.metrics) {
100
+ base.metrics.increment(Constants.CACHE_DELETED_TOTAL)
101
+ }
102
+ storage.delete(hashKey)
103
+ base.log.debug(`Delete ${hashKey}`)
104
+
105
+ return Promise.resolve()
106
+ },
107
+ clear (pattern = '**') {
108
+ if (base.metrics) {
109
+ base.metrics.increment(Constants.CACHE_DELETED_TOTAL)
110
+ }
111
+ storage.forEach((_, key) => {
112
+ if (match(key, pattern)) {
113
+ base.log.debug(`Delete ${key}`)
114
+ this.remove(key)
115
+ }
116
+ })
117
+ return Promise.resolve()
118
+ },
119
+ lock (key, ttl) {
120
+ return lock.acquire(key, ttl).then(() => {
121
+ return () => lock.release(key)
122
+ })
123
+ },
124
+ tryAcquireLock (key, ttl) {
125
+ if (lock.isLocked(key)) {
126
+ return Promise.reject(new Error('Locked'))
127
+ }
128
+
129
+ return lock.acquire(key, ttl).then(() => {
130
+ return () => lock.release(key)
131
+ })
132
+ },
133
+ stop () {
134
+ clearInterval(timer)
135
+ }
136
+ })
137
+ return cache
138
+ }
139
+
140
+ module.exports = { createInMemoryLruCache }
@@ -0,0 +1,46 @@
1
+ /*
2
+ * Author: Kevin Ries (kevin@fachw3rk.de)
3
+ * -----
4
+ * Copyright 2021 Fachwerk
5
+ */
6
+
7
+ module.exports = {
8
+ ...require('./base'),
9
+ ...require('./inMemory'),
10
+ ...require('./inMemoryLru')
11
+ }
12
+
13
+ // exports.resolve = (cacheOptions) => {
14
+ // const getByName = name => {
15
+ // if (!name) {
16
+ // return null
17
+ // }
18
+
19
+ // const n = Object
20
+ // .keys(adapters)
21
+ // .find(n => n.toLowerCase() === name.toLowerCase())
22
+
23
+ // if (n) {
24
+ // return adapters[n]
25
+ // }
26
+ // }
27
+
28
+ // let cacheFactory
29
+
30
+ // if (cacheOptions === true) {
31
+ // cacheFactory = this.adapters.Memory
32
+ // } else if (isString(cacheOptions)) {
33
+ // const cache = getByName(cacheOptions)
34
+
35
+ // if (cache) {
36
+ // cacheFactory = cache()
37
+ // } else {
38
+ // throw new WeaveBrokerOptionsError(`Unknown cache type "${cacheOptions}"`)
39
+ // }
40
+ // } else if (isFunction(cacheOptions)) {
41
+ // cacheFactory = cacheOptions
42
+ // }
43
+ // if (cacheFactory) {
44
+ // return cacheFactory
45
+ // }
46
+ // }
package/lib/cache/base.js CHANGED
@@ -1,10 +1,29 @@
1
1
  /*
2
2
  * Author: Kevin Ries (kevin@fachw3rk.de)
3
3
  * -----
4
- * Copyright 2020 Fachwerk
4
+ * Copyright 2021 Fachwerk
5
5
  */
6
6
  const crypto = require('crypto')
7
- const { isObject } = require('@weave-js/utils')
7
+ const { isObject, dotGet, isString } = require('@weave-js/utils')
8
+ const Constants = require('../metrics/constants')
9
+ const { WeaveError } = require('../errors')
10
+
11
+ /**
12
+ * Get property from data or metadata object.
13
+ * @param {any} data data object
14
+ * @param {object} metadata metadata object
15
+ * @param {string} key key
16
+ * @returns {any} Result
17
+ */
18
+ function getPropertyFromDataOrMetadata (data, metadata, key) {
19
+ // if a key starts with ":", the property is picked from metadata
20
+ if (key.startsWith(':')) {
21
+ // remove ':' from key.
22
+ key = key.replace(':', '')
23
+ return dotGet(metadata, key)
24
+ }
25
+ return dotGet(data, key)
26
+ }
8
27
 
9
28
  function getCacheKeyByObject (val) {
10
29
  if (Array.isArray(val)) {
@@ -28,20 +47,31 @@ function generateHash (key) {
28
47
  }
29
48
 
30
49
  function registerCacheMetrics (metrics) {
31
- // todo: register metric stores
50
+ metrics.register({ type: 'counter', name: Constants.CACHE_GET_TOTAL })
51
+ metrics.register({ type: 'counter', name: Constants.CACHE_SET_TOTAL })
52
+ metrics.register({ type: 'counter', name: Constants.CACHE_FOUND_TOTAL })
53
+ metrics.register({ type: 'counter', name: Constants.CACHE_EXPIRED_TOTAL })
54
+ metrics.register({ type: 'counter', name: Constants.CACHE_DELETED_TOTAL })
55
+ metrics.register({ type: 'counter', name: Constants.CACHE_CLEANED_TOTAL })
32
56
  }
33
57
 
34
- exports.createCacheBase = (runtime, options) => {
58
+ exports.createCacheBase = (name, runtime, options) => {
59
+ if (!isString(name)) {
60
+ throw new WeaveError('Name needs to be a string.')
61
+ }
62
+
35
63
  const cache = {
64
+ name,
65
+ isConnected: false,
66
+ runtime,
36
67
  options: Object.assign({
37
68
  ttl: null
38
69
  }, options),
39
70
  init () {
40
- this.runtime = runtime
41
- this.metrics = runtime.metrics
42
-
43
- if (this.runtime) {
44
- registerCacheMetrics(this.metrics)
71
+ // register metrics
72
+ if (runtime.metrics) {
73
+ this.metrics = runtime.metrics
74
+ registerCacheMetrics(runtime.metrics)
45
75
  }
46
76
  },
47
77
  log: runtime.createLogger('CACHER'),
@@ -65,20 +95,22 @@ exports.createCacheBase = (runtime, options) => {
65
95
  /* istanbul ignore next */
66
96
  return Promise.resolve()
67
97
  },
68
- getCachingHash (actionName, params, meta, keys) {
69
- if (params || meta) {
70
- const prefix = `${actionName}:`
98
+ getCachingKey (actionName, data, metadata, keys) {
99
+ if (data || metadata) {
100
+ const prefix = actionName + ':'
71
101
 
72
102
  if (keys) {
103
+ // fast path for single keys
73
104
  if (keys.length === 1) {
74
- const value = params[keys[0]]
105
+ const value = getPropertyFromDataOrMetadata(data, metadata, keys[0])
75
106
  const key = getCacheKeyByObject(value)
76
107
  return prefix + (isObject(value) ? key : value)
77
108
  }
78
109
 
110
+ // Handle data cache keys
79
111
  if (keys.length > 0) {
80
- const res = keys.reduce((pre, property, i) => {
81
- const value = params[property]
112
+ const res = keys.reduce((pre, property, index) => {
113
+ const value = getPropertyFromDataOrMetadata(data, metadata, property)
82
114
  let hash
83
115
  if (isObject(value)) {
84
116
  const key = getCacheKeyByObject(value)
@@ -87,12 +119,12 @@ exports.createCacheBase = (runtime, options) => {
87
119
  hash = value
88
120
  }
89
121
 
90
- return pre + (i > 0 ? '|' : '') + hash
122
+ return pre + (index > 0 ? '|' : '') + hash
91
123
  }, prefix)
92
124
  return res
93
125
  }
94
126
  } else {
95
- return prefix + generateHash(getCacheKeyByObject(params))
127
+ return prefix + generateHash(getCacheKeyByObject(data))
96
128
  }
97
129
  }
98
130
 
@@ -100,32 +132,5 @@ exports.createCacheBase = (runtime, options) => {
100
132
  }
101
133
  }
102
134
 
103
- cache.middleware = () => {
104
- return {
105
- localAction: (handler, action) => {
106
- const cacheOptions = Object.assign({ enabled: true }, isObject(action.cache) ? action.cache : { enabled: !!action.cache })
107
- if (cacheOptions.enabled) {
108
- return function cacheMiddleware (context) {
109
- const cacheHashKey = cache.getCachingHash(action.name, context.data, context.meta, action.cache.keys)
110
- context.isCachedResult = false
111
-
112
- return cache.get(cacheHashKey).then((content) => {
113
- if (content !== null) {
114
- context.isCachedResult = true
115
- return content
116
- }
117
-
118
- return handler(context).then((result) => {
119
- cache.set(cacheHashKey, result, action.cache.ttl)
120
- return result
121
- })
122
- })
123
- }
124
- }
125
- return handler
126
- }
127
- }
128
- }
129
-
130
135
  return cache
131
136
  }