@webex/webex-core 2.59.3-next.1 → 2.59.4

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 (189) hide show
  1. package/.eslintrc.js +6 -6
  2. package/README.md +79 -79
  3. package/babel.config.js +3 -3
  4. package/dist/config.js +24 -24
  5. package/dist/config.js.map +1 -1
  6. package/dist/credentials-config.js +56 -56
  7. package/dist/credentials-config.js.map +1 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/interceptors/auth.js +28 -28
  10. package/dist/interceptors/auth.js.map +1 -1
  11. package/dist/interceptors/default-options.js +24 -24
  12. package/dist/interceptors/default-options.js.map +1 -1
  13. package/dist/interceptors/embargo.js +9 -9
  14. package/dist/interceptors/embargo.js.map +1 -1
  15. package/dist/interceptors/network-timing.js +19 -19
  16. package/dist/interceptors/network-timing.js.map +1 -1
  17. package/dist/interceptors/payload-transformer.js +19 -19
  18. package/dist/interceptors/payload-transformer.js.map +1 -1
  19. package/dist/interceptors/rate-limit.js +40 -40
  20. package/dist/interceptors/rate-limit.js.map +1 -1
  21. package/dist/interceptors/redirect.js +13 -13
  22. package/dist/interceptors/redirect.js.map +1 -1
  23. package/dist/interceptors/request-event.js +23 -23
  24. package/dist/interceptors/request-event.js.map +1 -1
  25. package/dist/interceptors/request-logger.js +13 -13
  26. package/dist/interceptors/request-logger.js.map +1 -1
  27. package/dist/interceptors/request-timing.js +23 -23
  28. package/dist/interceptors/request-timing.js.map +1 -1
  29. package/dist/interceptors/response-logger.js +19 -19
  30. package/dist/interceptors/response-logger.js.map +1 -1
  31. package/dist/interceptors/user-agent.js +29 -29
  32. package/dist/interceptors/user-agent.js.map +1 -1
  33. package/dist/interceptors/webex-tracking-id.js +15 -15
  34. package/dist/interceptors/webex-tracking-id.js.map +1 -1
  35. package/dist/interceptors/webex-user-agent.js +13 -13
  36. package/dist/interceptors/webex-user-agent.js.map +1 -1
  37. package/dist/lib/batcher.js +83 -83
  38. package/dist/lib/batcher.js.map +1 -1
  39. package/dist/lib/credentials/credentials.js +103 -103
  40. package/dist/lib/credentials/credentials.js.map +1 -1
  41. package/dist/lib/credentials/grant-errors.js +17 -17
  42. package/dist/lib/credentials/grant-errors.js.map +1 -1
  43. package/dist/lib/credentials/index.js +2 -2
  44. package/dist/lib/credentials/index.js.map +1 -1
  45. package/dist/lib/credentials/scope.js +11 -11
  46. package/dist/lib/credentials/scope.js.map +1 -1
  47. package/dist/lib/credentials/token-collection.js +2 -2
  48. package/dist/lib/credentials/token-collection.js.map +1 -1
  49. package/dist/lib/credentials/token.js +145 -145
  50. package/dist/lib/credentials/token.js.map +1 -1
  51. package/dist/lib/page.js +49 -49
  52. package/dist/lib/page.js.map +1 -1
  53. package/dist/lib/services/constants.js.map +1 -1
  54. package/dist/lib/services/index.js +2 -2
  55. package/dist/lib/services/index.js.map +1 -1
  56. package/dist/lib/services/interceptors/server-error.js +9 -9
  57. package/dist/lib/services/interceptors/server-error.js.map +1 -1
  58. package/dist/lib/services/interceptors/service.js +24 -24
  59. package/dist/lib/services/interceptors/service.js.map +1 -1
  60. package/dist/lib/services/metrics.js.map +1 -1
  61. package/dist/lib/services/service-catalog.js +104 -104
  62. package/dist/lib/services/service-catalog.js.map +1 -1
  63. package/dist/lib/services/service-fed-ramp.js.map +1 -1
  64. package/dist/lib/services/service-host.js +134 -134
  65. package/dist/lib/services/service-host.js.map +1 -1
  66. package/dist/lib/services/service-registry.js +175 -175
  67. package/dist/lib/services/service-registry.js.map +1 -1
  68. package/dist/lib/services/service-state.js +38 -38
  69. package/dist/lib/services/service-state.js.map +1 -1
  70. package/dist/lib/services/service-url.js +31 -31
  71. package/dist/lib/services/service-url.js.map +1 -1
  72. package/dist/lib/services/services.js +245 -245
  73. package/dist/lib/services/services.js.map +1 -1
  74. package/dist/lib/stateless-webex-plugin.js +28 -28
  75. package/dist/lib/stateless-webex-plugin.js.map +1 -1
  76. package/dist/lib/storage/decorators.js +27 -27
  77. package/dist/lib/storage/decorators.js.map +1 -1
  78. package/dist/lib/storage/errors.js +4 -4
  79. package/dist/lib/storage/errors.js.map +1 -1
  80. package/dist/lib/storage/index.js.map +1 -1
  81. package/dist/lib/storage/make-webex-plugin-store.js +44 -44
  82. package/dist/lib/storage/make-webex-plugin-store.js.map +1 -1
  83. package/dist/lib/storage/make-webex-store.js +40 -40
  84. package/dist/lib/storage/make-webex-store.js.map +1 -1
  85. package/dist/lib/storage/memory-store-adapter.js +9 -9
  86. package/dist/lib/storage/memory-store-adapter.js.map +1 -1
  87. package/dist/lib/webex-core-plugin-mixin.js +13 -13
  88. package/dist/lib/webex-core-plugin-mixin.js.map +1 -1
  89. package/dist/lib/webex-http-error.js +9 -9
  90. package/dist/lib/webex-http-error.js.map +1 -1
  91. package/dist/lib/webex-internal-core-plugin-mixin.js +13 -13
  92. package/dist/lib/webex-internal-core-plugin-mixin.js.map +1 -1
  93. package/dist/lib/webex-plugin.js +36 -36
  94. package/dist/lib/webex-plugin.js.map +1 -1
  95. package/dist/plugins/logger.js +9 -9
  96. package/dist/plugins/logger.js.map +1 -1
  97. package/dist/webex-core.js +104 -104
  98. package/dist/webex-core.js.map +1 -1
  99. package/dist/webex-internal-core.js +12 -12
  100. package/dist/webex-internal-core.js.map +1 -1
  101. package/jest.config.js +3 -3
  102. package/package.json +19 -20
  103. package/process +1 -1
  104. package/src/config.js +90 -90
  105. package/src/credentials-config.js +212 -212
  106. package/src/index.js +62 -62
  107. package/src/interceptors/auth.js +186 -186
  108. package/src/interceptors/default-options.js +55 -55
  109. package/src/interceptors/embargo.js +43 -43
  110. package/src/interceptors/network-timing.js +54 -54
  111. package/src/interceptors/payload-transformer.js +55 -55
  112. package/src/interceptors/rate-limit.js +169 -169
  113. package/src/interceptors/redirect.js +106 -106
  114. package/src/interceptors/request-event.js +93 -93
  115. package/src/interceptors/request-logger.js +78 -78
  116. package/src/interceptors/request-timing.js +65 -65
  117. package/src/interceptors/response-logger.js +98 -98
  118. package/src/interceptors/user-agent.js +77 -77
  119. package/src/interceptors/webex-tracking-id.js +73 -73
  120. package/src/interceptors/webex-user-agent.js +79 -79
  121. package/src/lib/batcher.js +307 -307
  122. package/src/lib/credentials/credentials.js +552 -552
  123. package/src/lib/credentials/grant-errors.js +92 -92
  124. package/src/lib/credentials/index.js +16 -16
  125. package/src/lib/credentials/scope.js +34 -34
  126. package/src/lib/credentials/token-collection.js +17 -17
  127. package/src/lib/credentials/token.js +559 -559
  128. package/src/lib/page.js +159 -159
  129. package/src/lib/services/constants.js +9 -9
  130. package/src/lib/services/index.js +26 -26
  131. package/src/lib/services/interceptors/server-error.js +48 -48
  132. package/src/lib/services/interceptors/service.js +101 -101
  133. package/src/lib/services/metrics.js +4 -4
  134. package/src/lib/services/service-catalog.js +435 -435
  135. package/src/lib/services/service-fed-ramp.js +4 -4
  136. package/src/lib/services/service-host.js +267 -267
  137. package/src/lib/services/service-registry.js +465 -465
  138. package/src/lib/services/service-state.js +78 -78
  139. package/src/lib/services/service-url.js +124 -124
  140. package/src/lib/services/services.js +1018 -1018
  141. package/src/lib/stateless-webex-plugin.js +98 -98
  142. package/src/lib/storage/decorators.js +220 -220
  143. package/src/lib/storage/errors.js +15 -15
  144. package/src/lib/storage/index.js +10 -10
  145. package/src/lib/storage/make-webex-plugin-store.js +211 -211
  146. package/src/lib/storage/make-webex-store.js +140 -140
  147. package/src/lib/storage/memory-store-adapter.js +79 -79
  148. package/src/lib/webex-core-plugin-mixin.js +114 -114
  149. package/src/lib/webex-http-error.js +61 -61
  150. package/src/lib/webex-internal-core-plugin-mixin.js +107 -107
  151. package/src/lib/webex-plugin.js +222 -222
  152. package/src/plugins/logger.js +60 -60
  153. package/src/webex-core.js +745 -745
  154. package/src/webex-internal-core.js +46 -46
  155. package/test/integration/spec/credentials/credentials.js +139 -139
  156. package/test/integration/spec/credentials/token.js +102 -102
  157. package/test/integration/spec/services/service-catalog.js +838 -838
  158. package/test/integration/spec/services/services.js +1221 -1221
  159. package/test/integration/spec/webex-core.js +178 -178
  160. package/test/unit/spec/_setup.js +44 -44
  161. package/test/unit/spec/credentials/credentials.js +1017 -1017
  162. package/test/unit/spec/credentials/token.js +441 -441
  163. package/test/unit/spec/interceptors/auth.js +521 -521
  164. package/test/unit/spec/interceptors/default-options.js +84 -84
  165. package/test/unit/spec/interceptors/embargo.js +144 -144
  166. package/test/unit/spec/interceptors/network-timing.js +49 -49
  167. package/test/unit/spec/interceptors/payload-transformer.js +155 -155
  168. package/test/unit/spec/interceptors/rate-limit.js +302 -302
  169. package/test/unit/spec/interceptors/redirect.js +102 -102
  170. package/test/unit/spec/interceptors/request-timing.js +92 -92
  171. package/test/unit/spec/interceptors/user-agent.js +76 -76
  172. package/test/unit/spec/interceptors/webex-tracking-id.js +76 -76
  173. package/test/unit/spec/interceptors/webex-user-agent.js +159 -159
  174. package/test/unit/spec/lib/batcher.js +330 -330
  175. package/test/unit/spec/lib/page.js +148 -148
  176. package/test/unit/spec/lib/webex-plugin.js +48 -48
  177. package/test/unit/spec/services/interceptors/server-error.js +204 -204
  178. package/test/unit/spec/services/interceptors/service.js +188 -188
  179. package/test/unit/spec/services/service-catalog.js +194 -194
  180. package/test/unit/spec/services/service-host.js +260 -260
  181. package/test/unit/spec/services/service-registry.js +747 -747
  182. package/test/unit/spec/services/service-state.js +60 -60
  183. package/test/unit/spec/services/service-url.js +258 -258
  184. package/test/unit/spec/services/services.js +348 -348
  185. package/test/unit/spec/storage/persist.js +50 -50
  186. package/test/unit/spec/storage/storage-adapter.js +12 -12
  187. package/test/unit/spec/storage/wait-for-value.js +81 -81
  188. package/test/unit/spec/webex-core.js +253 -253
  189. package/test/unit/spec/webex-internal-core.js +91 -91
@@ -1,98 +1,98 @@
1
- import {readonly} from 'core-decorators';
2
- import AmpEvents from 'ampersand-events';
3
-
4
- // hold onto weak references to parent' Webexs to help avoid retain cycles
5
- const webexs = new WeakMap();
6
-
7
- /**
8
- * Base plugin class. Doesn't depend on Ampersand State
9
- */
10
- export default class StatelessWebexPlugin {
11
- /**
12
- * The config for this plugin's namespace. If no namespace has been set
13
- * returns all of webex's config.
14
- * @type {Object}
15
- */
16
- get config() {
17
- let namespace = this.getNamespace ? this.getNamespace() : this.namespace;
18
-
19
- if (namespace) {
20
- namespace = namespace.toLowerCase();
21
-
22
- return this.webex.config[namespace];
23
- }
24
-
25
- return this.webex.config;
26
- }
27
-
28
- /**
29
- * A reference to the webex logger.
30
- * @type {Logger}
31
- */
32
- get logger() {
33
- return this.webex.logger;
34
- }
35
-
36
- /**
37
- * A reference to the main sdk instance
38
- * @type {WebexCore}
39
- */
40
- get webex() {
41
- return webexs.get(this);
42
- }
43
-
44
- @readonly
45
- /**
46
- * Mostly here for compatibility with legacy WebexPlugins.
47
- * StatelessWebexPlugins will never have a state other than ready, however, if
48
- * we implement stateful WebexPlugins as es6 classes, they may have the option
49
- * to have ready be false
50
- * @private
51
- * @type {boolean}
52
- */
53
- ready = true;
54
-
55
- /**
56
- * Constructor. One of attrs.webex or options.parent is required
57
- * @param {Object} attrs
58
- * @param {WebexCore} [attrs.webex]
59
- * @param {Object} options
60
- * @param {WebexCore} [options.parent]
61
- */
62
- constructor(attrs = {}, options = {}) {
63
- let webex = attrs.webex || options.parent;
64
-
65
- if (!webex) {
66
- throw new Error(
67
- 'One of `attrs.webex` or `options.parent` must be supplied when initializing a StatelessWebexPlugin'
68
- );
69
- }
70
-
71
- while (webex.parent || webex.collection) {
72
- webex = webex.parent || webex.collection;
73
- }
74
- webexs.set(this, webex);
75
- }
76
-
77
- /**
78
- * Proxies to {@link WebexPlugin#webex}'s `request()` method.
79
- * @see WebexCore#request
80
- * @param {Array<mixed>} args
81
- * @returns {Promise}
82
- */
83
- request(...args) {
84
- return this.webex.request(...args);
85
- }
86
-
87
- /**
88
- * Proxies to {@link WebexPlugin#webex}'s `upload()` method.
89
- * @see WebexCore#upload
90
- * @param {Array<mixed>} args
91
- * @returns {Promise}
92
- */
93
- upload(...args) {
94
- return this.webex.upload(...args);
95
- }
96
- }
97
-
98
- Object.assign(StatelessWebexPlugin.prototype, AmpEvents);
1
+ import {readonly} from 'core-decorators';
2
+ import AmpEvents from 'ampersand-events';
3
+
4
+ // hold onto weak references to parent' Webexs to help avoid retain cycles
5
+ const webexs = new WeakMap();
6
+
7
+ /**
8
+ * Base plugin class. Doesn't depend on Ampersand State
9
+ */
10
+ export default class StatelessWebexPlugin {
11
+ /**
12
+ * The config for this plugin's namespace. If no namespace has been set
13
+ * returns all of webex's config.
14
+ * @type {Object}
15
+ */
16
+ get config() {
17
+ let namespace = this.getNamespace ? this.getNamespace() : this.namespace;
18
+
19
+ if (namespace) {
20
+ namespace = namespace.toLowerCase();
21
+
22
+ return this.webex.config[namespace];
23
+ }
24
+
25
+ return this.webex.config;
26
+ }
27
+
28
+ /**
29
+ * A reference to the webex logger.
30
+ * @type {Logger}
31
+ */
32
+ get logger() {
33
+ return this.webex.logger;
34
+ }
35
+
36
+ /**
37
+ * A reference to the main sdk instance
38
+ * @type {WebexCore}
39
+ */
40
+ get webex() {
41
+ return webexs.get(this);
42
+ }
43
+
44
+ @readonly
45
+ /**
46
+ * Mostly here for compatibility with legacy WebexPlugins.
47
+ * StatelessWebexPlugins will never have a state other than ready, however, if
48
+ * we implement stateful WebexPlugins as es6 classes, they may have the option
49
+ * to have ready be false
50
+ * @private
51
+ * @type {boolean}
52
+ */
53
+ ready = true;
54
+
55
+ /**
56
+ * Constructor. One of attrs.webex or options.parent is required
57
+ * @param {Object} attrs
58
+ * @param {WebexCore} [attrs.webex]
59
+ * @param {Object} options
60
+ * @param {WebexCore} [options.parent]
61
+ */
62
+ constructor(attrs = {}, options = {}) {
63
+ let webex = attrs.webex || options.parent;
64
+
65
+ if (!webex) {
66
+ throw new Error(
67
+ 'One of `attrs.webex` or `options.parent` must be supplied when initializing a StatelessWebexPlugin'
68
+ );
69
+ }
70
+
71
+ while (webex.parent || webex.collection) {
72
+ webex = webex.parent || webex.collection;
73
+ }
74
+ webexs.set(this, webex);
75
+ }
76
+
77
+ /**
78
+ * Proxies to {@link WebexPlugin#webex}'s `request()` method.
79
+ * @see WebexCore#request
80
+ * @param {Array<mixed>} args
81
+ * @returns {Promise}
82
+ */
83
+ request(...args) {
84
+ return this.webex.request(...args);
85
+ }
86
+
87
+ /**
88
+ * Proxies to {@link WebexPlugin#webex}'s `upload()` method.
89
+ * @see WebexCore#upload
90
+ * @param {Array<mixed>} args
91
+ * @returns {Promise}
92
+ */
93
+ upload(...args) {
94
+ return this.webex.upload(...args);
95
+ }
96
+ }
97
+
98
+ Object.assign(StatelessWebexPlugin.prototype, AmpEvents);
@@ -1,220 +1,220 @@
1
- /*!
2
- * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
- */
4
-
5
- /* eslint no-invalid-this: [0] */
6
-
7
- import {curry, debounce, identity, result, wrap} from 'lodash';
8
- import {make} from '@webex/common';
9
-
10
- import {NotFoundError} from './errors';
11
-
12
- /**
13
- * Stores the result of fn before returning it
14
- * @param {string} key
15
- * @private
16
- * @returns {Promise} resolves with the result of fn
17
- */
18
- export function persist(...args) {
19
- if (args.length === 3) {
20
- return persist('@')(...args);
21
- }
22
-
23
- const [key, decider] = args;
24
-
25
- return function persistDecorator(target, prop, descriptor) {
26
- if (prop !== 'initialize') {
27
- // Once we have class-based alternative to AmpersandState, it should be
28
- // detected here.
29
- throw new TypeError(
30
- '@persist can only currently be applied to AmpersandState objects or their derivatives and must be applied to the initialize method'
31
- );
32
- }
33
-
34
- descriptor.value = wrap(descriptor.value, function persistExecutor(fn, ...initializeArgs) {
35
- const ret = Reflect.apply(fn, this, initializeArgs);
36
- const changeEvent = key === '@' ? 'change' : `change:${key}`;
37
-
38
- // Some scenarios will lead to lots of change events on a single tick; we
39
- // really only care about writing once things have stopped changing. with
40
- // a debounce of zero, we're effectively coalescing all the changes
41
- // triggered by a single call to set() and commiting them on the next tick
42
- // eslint-disable-next-line no-invalid-this
43
- this.on(
44
- changeEvent,
45
- debounce(() => {
46
- const shouldPersist = !decider || Reflect.apply(decider, this, ...initializeArgs);
47
-
48
- if (!shouldPersist) {
49
- return Promise.resolve();
50
- }
51
- if (key === '@') {
52
- // eslint-disable-next-line no-invalid-this
53
- return this.boundedStorage.put(key, this);
54
- }
55
-
56
- // eslint-disable-next-line no-invalid-this
57
- return this.boundedStorage.put(key, this[key]);
58
- }, 0)
59
- );
60
-
61
- return ret;
62
- });
63
-
64
- prepareInitialize(target, prop);
65
- };
66
- }
67
-
68
- const M = Map;
69
- const S = Set;
70
- const BlockingKeyMap = make(M, M, S);
71
- const blockingKeys = new BlockingKeyMap();
72
-
73
- /**
74
- * Prevents fn from executing until key has been (attempted to be) loaded
75
- * @param {string} key
76
- * @param {Function} fn
77
- * @private
78
- * @returns {Promise} result of fn
79
- */
80
- export function waitForValue(key) {
81
- if (!key) {
82
- throw new Error('`key` is required');
83
- }
84
-
85
- return function waitForValueDecorator(target, prop, descriptor) {
86
- blockingKeys.add(target, prop, key);
87
- descriptor.value = wrap(descriptor.value, function waitForValueExecutor(fn, ...args) {
88
- const keys = blockingKeys.get(target, prop);
89
-
90
- return Promise.all([...keys].map((k) => this.boundedStorage.waitFor(k))).then(() =>
91
- Reflect.apply(fn, this, args)
92
- );
93
- });
94
-
95
- // This *should* make decorators compatible with AmpersandState class
96
- // definitions
97
- if (typeof target === 'object' && !target.prototype) {
98
- target[prop] = descriptor.value;
99
- }
100
-
101
- prepareInitialize(target, prop);
102
-
103
- return descriptor;
104
- };
105
- }
106
-
107
- const inited = new Set();
108
-
109
- /**
110
- * finds a means of identitying the `target` param passed to
111
- * `prepareInitialize()`. When possible, avoids duplicate `init()` calls if
112
- * namespaces collide
113
- *
114
- * @param {Object|Constructor} target
115
- * @private
116
- * @returns {String|Constructor}
117
- */
118
- function identifyTarget(target) {
119
- if (target.namespace) {
120
- return target.namespace;
121
- }
122
-
123
- return target;
124
- }
125
-
126
- const stack = new Set();
127
-
128
- /**
129
- * @param {Function} target
130
- * @param {string} prop
131
- * @private
132
- * @returns {undefined}
133
- */
134
- function prepareInitialize(target, prop) {
135
- const id = identifyTarget(target);
136
-
137
- if (!inited.has(id)) {
138
- inited.add(id);
139
- if (target.initialize) {
140
- target.initialize = wrap(target.initialize, function applyInit(fn, ...args) {
141
- const ret = Reflect.apply(fn, this, args);
142
-
143
- Reflect.apply(init, this, args);
144
-
145
- return ret;
146
- });
147
-
148
- return;
149
- }
150
-
151
- target.initialize = init;
152
- }
153
-
154
- /**
155
- * @private
156
- * @returns {undefined}
157
- */
158
- function init() {
159
- const self = this;
160
- const namespace = this.getNamespace();
161
-
162
- this.webex.initialize = wrap(
163
- this.webex.initialize || identity,
164
- function applyInit(fn, ...args) {
165
- // Call webex's initalize method first
166
- // Reminder: in order for MockWebex to accept initial storage data, the
167
- // wrapped initialize() must be invoked before attempting to load data.
168
- // Reminder: context here is `webex`, not `self`.
169
- stack.add(namespace);
170
- Reflect.apply(fn, this, args);
171
-
172
- // Then prepare a function for setting values retrieved from storage
173
- const set = curry((key, value) => {
174
- this.logger.debug(`storage:(${namespace}): got \`${key}\` for first time`);
175
- if (key === '@') {
176
- self.parent.set({
177
- [namespace.toLowerCase()]: value,
178
- });
179
- } else if (result(self[key], 'isState')) {
180
- self[key].set(value);
181
- } else {
182
- self.set(key, value);
183
- }
184
- this.logger.debug(`storage:(${namespace}): set \`${key}\` for first time`);
185
- });
186
-
187
- // And prepare an error handler for when those keys can't be found
188
- const handle = curry((key, reason) => {
189
- if (
190
- reason instanceof NotFoundError ||
191
- (process.env.NODE_ENV !== 'production' &&
192
- reason.toString().includes('MockNotFoundError'))
193
- ) {
194
- this.logger.debug(`storage(${namespace}): no data for \`${key}\`, continuing`);
195
-
196
- return Promise.resolve();
197
- }
198
- this.logger.error(`storage(${namespace}): failed to init \`${key}\``, reason);
199
-
200
- return Promise.reject(reason);
201
- });
202
-
203
- // Iterate over the list of keys marked as blocking via `@waitForValue`
204
- const keys = blockingKeys.get(target, prop);
205
- const promises = [];
206
-
207
- keys.forEach((key) => {
208
- promises.push(this.boundedStorage.get(namespace, key).then(set(key)).catch(handle(key)));
209
- });
210
-
211
- Promise.all(promises).then(() => {
212
- stack.delete(namespace);
213
- if (stack.size === 0) {
214
- this.loaded = true;
215
- }
216
- });
217
- }
218
- );
219
- }
220
- }
1
+ /*!
2
+ * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+
5
+ /* eslint no-invalid-this: [0] */
6
+
7
+ import {curry, debounce, identity, result, wrap} from 'lodash';
8
+ import {make} from '@webex/common';
9
+
10
+ import {NotFoundError} from './errors';
11
+
12
+ /**
13
+ * Stores the result of fn before returning it
14
+ * @param {string} key
15
+ * @private
16
+ * @returns {Promise} resolves with the result of fn
17
+ */
18
+ export function persist(...args) {
19
+ if (args.length === 3) {
20
+ return persist('@')(...args);
21
+ }
22
+
23
+ const [key, decider] = args;
24
+
25
+ return function persistDecorator(target, prop, descriptor) {
26
+ if (prop !== 'initialize') {
27
+ // Once we have class-based alternative to AmpersandState, it should be
28
+ // detected here.
29
+ throw new TypeError(
30
+ '@persist can only currently be applied to AmpersandState objects or their derivatives and must be applied to the initialize method'
31
+ );
32
+ }
33
+
34
+ descriptor.value = wrap(descriptor.value, function persistExecutor(fn, ...initializeArgs) {
35
+ const ret = Reflect.apply(fn, this, initializeArgs);
36
+ const changeEvent = key === '@' ? 'change' : `change:${key}`;
37
+
38
+ // Some scenarios will lead to lots of change events on a single tick; we
39
+ // really only care about writing once things have stopped changing. with
40
+ // a debounce of zero, we're effectively coalescing all the changes
41
+ // triggered by a single call to set() and commiting them on the next tick
42
+ // eslint-disable-next-line no-invalid-this
43
+ this.on(
44
+ changeEvent,
45
+ debounce(() => {
46
+ const shouldPersist = !decider || Reflect.apply(decider, this, ...initializeArgs);
47
+
48
+ if (!shouldPersist) {
49
+ return Promise.resolve();
50
+ }
51
+ if (key === '@') {
52
+ // eslint-disable-next-line no-invalid-this
53
+ return this.boundedStorage.put(key, this);
54
+ }
55
+
56
+ // eslint-disable-next-line no-invalid-this
57
+ return this.boundedStorage.put(key, this[key]);
58
+ }, 0)
59
+ );
60
+
61
+ return ret;
62
+ });
63
+
64
+ prepareInitialize(target, prop);
65
+ };
66
+ }
67
+
68
+ const M = Map;
69
+ const S = Set;
70
+ const BlockingKeyMap = make(M, M, S);
71
+ const blockingKeys = new BlockingKeyMap();
72
+
73
+ /**
74
+ * Prevents fn from executing until key has been (attempted to be) loaded
75
+ * @param {string} key
76
+ * @param {Function} fn
77
+ * @private
78
+ * @returns {Promise} result of fn
79
+ */
80
+ export function waitForValue(key) {
81
+ if (!key) {
82
+ throw new Error('`key` is required');
83
+ }
84
+
85
+ return function waitForValueDecorator(target, prop, descriptor) {
86
+ blockingKeys.add(target, prop, key);
87
+ descriptor.value = wrap(descriptor.value, function waitForValueExecutor(fn, ...args) {
88
+ const keys = blockingKeys.get(target, prop);
89
+
90
+ return Promise.all([...keys].map((k) => this.boundedStorage.waitFor(k))).then(() =>
91
+ Reflect.apply(fn, this, args)
92
+ );
93
+ });
94
+
95
+ // This *should* make decorators compatible with AmpersandState class
96
+ // definitions
97
+ if (typeof target === 'object' && !target.prototype) {
98
+ target[prop] = descriptor.value;
99
+ }
100
+
101
+ prepareInitialize(target, prop);
102
+
103
+ return descriptor;
104
+ };
105
+ }
106
+
107
+ const inited = new Set();
108
+
109
+ /**
110
+ * finds a means of identitying the `target` param passed to
111
+ * `prepareInitialize()`. When possible, avoids duplicate `init()` calls if
112
+ * namespaces collide
113
+ *
114
+ * @param {Object|Constructor} target
115
+ * @private
116
+ * @returns {String|Constructor}
117
+ */
118
+ function identifyTarget(target) {
119
+ if (target.namespace) {
120
+ return target.namespace;
121
+ }
122
+
123
+ return target;
124
+ }
125
+
126
+ const stack = new Set();
127
+
128
+ /**
129
+ * @param {Function} target
130
+ * @param {string} prop
131
+ * @private
132
+ * @returns {undefined}
133
+ */
134
+ function prepareInitialize(target, prop) {
135
+ const id = identifyTarget(target);
136
+
137
+ if (!inited.has(id)) {
138
+ inited.add(id);
139
+ if (target.initialize) {
140
+ target.initialize = wrap(target.initialize, function applyInit(fn, ...args) {
141
+ const ret = Reflect.apply(fn, this, args);
142
+
143
+ Reflect.apply(init, this, args);
144
+
145
+ return ret;
146
+ });
147
+
148
+ return;
149
+ }
150
+
151
+ target.initialize = init;
152
+ }
153
+
154
+ /**
155
+ * @private
156
+ * @returns {undefined}
157
+ */
158
+ function init() {
159
+ const self = this;
160
+ const namespace = this.getNamespace();
161
+
162
+ this.webex.initialize = wrap(
163
+ this.webex.initialize || identity,
164
+ function applyInit(fn, ...args) {
165
+ // Call webex's initalize method first
166
+ // Reminder: in order for MockWebex to accept initial storage data, the
167
+ // wrapped initialize() must be invoked before attempting to load data.
168
+ // Reminder: context here is `webex`, not `self`.
169
+ stack.add(namespace);
170
+ Reflect.apply(fn, this, args);
171
+
172
+ // Then prepare a function for setting values retrieved from storage
173
+ const set = curry((key, value) => {
174
+ this.logger.debug(`storage:(${namespace}): got \`${key}\` for first time`);
175
+ if (key === '@') {
176
+ self.parent.set({
177
+ [namespace.toLowerCase()]: value,
178
+ });
179
+ } else if (result(self[key], 'isState')) {
180
+ self[key].set(value);
181
+ } else {
182
+ self.set(key, value);
183
+ }
184
+ this.logger.debug(`storage:(${namespace}): set \`${key}\` for first time`);
185
+ });
186
+
187
+ // And prepare an error handler for when those keys can't be found
188
+ const handle = curry((key, reason) => {
189
+ if (
190
+ reason instanceof NotFoundError ||
191
+ (process.env.NODE_ENV !== 'production' &&
192
+ reason.toString().includes('MockNotFoundError'))
193
+ ) {
194
+ this.logger.debug(`storage(${namespace}): no data for \`${key}\`, continuing`);
195
+
196
+ return Promise.resolve();
197
+ }
198
+ this.logger.error(`storage(${namespace}): failed to init \`${key}\``, reason);
199
+
200
+ return Promise.reject(reason);
201
+ });
202
+
203
+ // Iterate over the list of keys marked as blocking via `@waitForValue`
204
+ const keys = blockingKeys.get(target, prop);
205
+ const promises = [];
206
+
207
+ keys.forEach((key) => {
208
+ promises.push(this.boundedStorage.get(namespace, key).then(set(key)).catch(handle(key)));
209
+ });
210
+
211
+ Promise.all(promises).then(() => {
212
+ stack.delete(namespace);
213
+ if (stack.size === 0) {
214
+ this.loaded = true;
215
+ }
216
+ });
217
+ }
218
+ );
219
+ }
220
+ }
@@ -1,15 +1,15 @@
1
- /*!
2
- * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
- */
4
-
5
- import {Exception} from '@webex/common';
6
-
7
- /**
8
- * General storage layer error
9
- */
10
- export class StorageError extends Exception {}
11
-
12
- /**
13
- * StorageError thrown when the storage layer does not contain the request key
14
- */
15
- export class NotFoundError extends StorageError {}
1
+ /*!
2
+ * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+
5
+ import {Exception} from '@webex/common';
6
+
7
+ /**
8
+ * General storage layer error
9
+ */
10
+ export class StorageError extends Exception {}
11
+
12
+ /**
13
+ * StorageError thrown when the storage layer does not contain the request key
14
+ */
15
+ export class NotFoundError extends StorageError {}