stoplight 5.8.3 → 6.0.0.rc2

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 (323) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -1
  3. data/LICENSE.md +1 -0
  4. data/README.md +132 -79
  5. data/UPGRADING.md +265 -6
  6. data/lib/stoplight/admin/actions/action.rb +4 -12
  7. data/lib/stoplight/admin/actions/lock.rb +12 -9
  8. data/lib/stoplight/admin/actions/lock_all.rb +27 -0
  9. data/lib/stoplight/admin/actions/remove.rb +12 -9
  10. data/lib/stoplight/admin/actions/stats.rb +15 -12
  11. data/lib/stoplight/admin/actions/unlock.rb +10 -9
  12. data/lib/stoplight/admin/assets/apple-touch-icon.png +0 -0
  13. data/lib/stoplight/admin/assets/favicon.ico +0 -0
  14. data/lib/stoplight/admin/assets/flowbite.min.css +2 -0
  15. data/lib/stoplight/admin/assets/flowbite.min.js +1 -0
  16. data/lib/stoplight/admin/assets/icon.svg +7 -0
  17. data/lib/stoplight/admin/assets/turbo.es2017-esm.min.js +34 -0
  18. data/lib/stoplight/admin/config_registry.rb +40 -0
  19. data/lib/stoplight/admin/dependencies.rb +29 -28
  20. data/lib/stoplight/admin/helpers.rb +50 -17
  21. data/lib/stoplight/admin/light_view.rb +184 -0
  22. data/lib/stoplight/admin/lights_stats.rb +7 -9
  23. data/lib/stoplight/admin/views/_card.erb +19 -21
  24. data/lib/stoplight/admin/views/index.erb +9 -3
  25. data/lib/stoplight/admin/views/layout.erb +45 -13
  26. data/lib/stoplight/admin.rb +110 -21
  27. data/lib/stoplight/data_store.rb +18 -8
  28. data/lib/stoplight/domain/config.rb +4 -1
  29. data/lib/stoplight/domain/error_tracking_policy.rb +9 -0
  30. data/lib/stoplight/domain/failure.rb +4 -0
  31. data/lib/stoplight/domain/id.rb +13 -0
  32. data/lib/stoplight/domain/light.rb +35 -77
  33. data/lib/stoplight/domain/lock_control.rb +55 -0
  34. data/lib/stoplight/domain/matcher_validator.rb +24 -0
  35. data/lib/stoplight/domain/state_snapshot.rb +2 -6
  36. data/lib/stoplight/domain/strategies/green_run_strategy.rb +33 -19
  37. data/lib/stoplight/domain/strategies/red_run_strategy.rb +8 -2
  38. data/lib/stoplight/domain/strategies/yellow_run_strategy.rb +56 -32
  39. data/lib/stoplight/domain/telemetry/bus.rb +90 -0
  40. data/lib/stoplight/domain/telemetry/consumer.rb +18 -0
  41. data/lib/stoplight/domain/telemetry/emitter.rb +45 -0
  42. data/lib/stoplight/domain/telemetry/envelope.rb +15 -0
  43. data/lib/stoplight/domain/telemetry/failure.rb +13 -0
  44. data/lib/stoplight/domain/telemetry/light_registered.rb +12 -0
  45. data/lib/stoplight/domain/telemetry/lock_changed.rb +19 -0
  46. data/lib/stoplight/domain/telemetry/metrics.rb +15 -0
  47. data/lib/stoplight/domain/telemetry/recovery_failed.rb +20 -0
  48. data/lib/stoplight/domain/telemetry/recovery_probe_completed.rb +15 -0
  49. data/lib/stoplight/domain/telemetry/recovery_started.rb +18 -0
  50. data/lib/stoplight/domain/telemetry/recovery_succeeded.rb +19 -0
  51. data/lib/stoplight/domain/telemetry/run_completed.rb +17 -0
  52. data/lib/stoplight/domain/telemetry/run_recorder.rb +58 -0
  53. data/lib/stoplight/domain/telemetry/settings.rb +21 -0
  54. data/lib/stoplight/domain/telemetry/state_transitioned.rb +11 -0
  55. data/lib/stoplight/domain/telemetry/subscription.rb +11 -0
  56. data/lib/stoplight/domain/telemetry/traffic_breached.rb +20 -0
  57. data/lib/stoplight/domain/tracker/recovery_probe.rb +61 -17
  58. data/lib/stoplight/domain/tracker/request.rb +17 -8
  59. data/lib/stoplight/domain/traffic_control/consecutive_errors.rb +13 -3
  60. data/lib/stoplight/domain/traffic_control/error_rate.rb +16 -12
  61. data/lib/stoplight/domain/traffic_recovery/consecutive_successes.rb +9 -3
  62. data/lib/stoplight/error.rb +10 -1
  63. data/lib/stoplight/infrastructure/config_serializer.rb +50 -0
  64. data/lib/stoplight/infrastructure/fail_safe/error_notifier.rb +25 -0
  65. data/lib/stoplight/infrastructure/fail_safe/storage/registry.rb +68 -0
  66. data/lib/stoplight/infrastructure/memory/storage/registry.rb +18 -0
  67. data/lib/stoplight/infrastructure/memory/storage/unbounded_metrics.rb +29 -32
  68. data/lib/stoplight/infrastructure/memory/storage/window_metrics/sliding_window.rb +74 -0
  69. data/lib/stoplight/infrastructure/memory/storage/window_metrics.rb +25 -24
  70. data/lib/stoplight/infrastructure/notifier/fail_safe.rb +3 -8
  71. data/lib/stoplight/infrastructure/notifier/generic.rb +3 -3
  72. data/lib/stoplight/infrastructure/redis/key.rb +25 -0
  73. data/lib/stoplight/infrastructure/redis/storage/now.lua +9 -0
  74. data/lib/stoplight/infrastructure/redis/storage/recovery_lock.rb +2 -4
  75. data/lib/stoplight/infrastructure/redis/storage/recovery_metrics.rb +1 -1
  76. data/lib/stoplight/infrastructure/redis/storage/registry.rb +73 -0
  77. data/lib/stoplight/infrastructure/redis/storage/scripting.rb +54 -3
  78. data/lib/stoplight/infrastructure/redis/storage/state/transition_to_green.lua +3 -1
  79. data/lib/stoplight/infrastructure/redis/storage/state/transition_to_red.lua +4 -2
  80. data/lib/stoplight/infrastructure/redis/storage/state/transition_to_yellow.lua +3 -1
  81. data/lib/stoplight/infrastructure/redis/storage/state.rb +8 -8
  82. data/lib/stoplight/infrastructure/redis/storage/unbounded_metrics/record_failure.lua +20 -21
  83. data/lib/stoplight/infrastructure/redis/storage/unbounded_metrics/record_success.lua +11 -19
  84. data/lib/stoplight/infrastructure/redis/storage/unbounded_metrics.rb +20 -16
  85. data/lib/stoplight/infrastructure/redis/storage/window_metrics/_evict.lua +44 -0
  86. data/lib/stoplight/infrastructure/redis/storage/window_metrics/metrics_snapshot.lua +14 -21
  87. data/lib/stoplight/infrastructure/redis/storage/window_metrics/record_failure.lua +38 -36
  88. data/lib/stoplight/infrastructure/redis/storage/window_metrics/record_success.lua +23 -30
  89. data/lib/stoplight/infrastructure/redis/storage/window_metrics.rb +45 -135
  90. data/lib/stoplight/infrastructure/system_clock.rb +4 -0
  91. data/lib/stoplight/telemetry.rb +19 -0
  92. data/lib/stoplight/types.rb +15 -2
  93. data/lib/stoplight/version.rb +1 -1
  94. data/lib/stoplight/wiring/config_compatibility_validator.rb +25 -2
  95. data/lib/stoplight/wiring/default.rb +1 -1
  96. data/lib/stoplight/wiring/default_config.rb +2 -1
  97. data/lib/stoplight/wiring/default_configuration.rb +3 -2
  98. data/lib/stoplight/wiring/external_caller.rb +27 -0
  99. data/lib/stoplight/wiring/fail_safe_config.rb +11 -0
  100. data/lib/stoplight/wiring/global_state.rb +93 -0
  101. data/lib/stoplight/wiring/{light_factory → light_configuration_dsl}/traffic_control_dsl.rb +3 -5
  102. data/lib/stoplight/wiring/{light_factory → light_configuration_dsl}/traffic_recovery_dsl.rb +2 -2
  103. data/lib/stoplight/wiring/light_configuration_dsl.rb +97 -0
  104. data/lib/stoplight/wiring/light_factory.rb +154 -97
  105. data/lib/stoplight/wiring/notifier_bridge.rb +33 -0
  106. data/lib/stoplight/wiring/redis/backend.rb +1 -1
  107. data/lib/stoplight/wiring/system/storage.rb +69 -0
  108. data/lib/stoplight/wiring/system.rb +125 -56
  109. data/lib/stoplight/wiring/{configuration_dsl.rb → system_configuration_dsl.rb} +13 -43
  110. data/lib/stoplight.rb +50 -194
  111. data/sig/sinatra/base.rbs +6 -0
  112. data/sig/stdlib/securerandom.rbs +4 -0
  113. data/sig/stoplight/admin/actions/action.rbs +10 -0
  114. data/sig/stoplight/admin/actions/lock.rbs +17 -0
  115. data/sig/stoplight/admin/actions/lock_all.rbs +17 -0
  116. data/sig/stoplight/admin/actions/remove.rbs +22 -0
  117. data/sig/stoplight/admin/actions/stats.rbs +28 -0
  118. data/sig/stoplight/admin/actions/unlock.rbs +17 -0
  119. data/sig/stoplight/admin/config_registry.rbs +20 -0
  120. data/sig/stoplight/admin/dependencies.rbs +16 -0
  121. data/sig/stoplight/admin/helpers.rbs +17 -0
  122. data/sig/stoplight/admin/light_view.rbs +39 -0
  123. data/sig/stoplight/admin/lights_stats.rbs +30 -0
  124. data/sig/stoplight/admin.rbs +14 -0
  125. data/sig/stoplight/color.rbs +3 -3
  126. data/sig/{_private/stoplight/data_store.rbs → stoplight/data_store/base.rbs} +1 -0
  127. data/sig/stoplight/data_store/memory.rbs +9 -0
  128. data/sig/stoplight/data_store/redis.rbs +17 -0
  129. data/sig/{_private/stoplight → stoplight}/domain/config.rbs +28 -24
  130. data/sig/{_private/stoplight → stoplight}/domain/error_tracking_policy.rbs +4 -0
  131. data/sig/{_private/stoplight → stoplight}/domain/failure.rbs +2 -0
  132. data/sig/stoplight/domain/id.rbs +7 -0
  133. data/sig/stoplight/domain/light.rbs +39 -0
  134. data/sig/{_private/stoplight → stoplight}/domain/light_info.rbs +1 -5
  135. data/sig/stoplight/domain/lock_control.rbs +16 -0
  136. data/sig/stoplight/domain/matcher_validator.rbs +11 -0
  137. data/sig/{_private/stoplight → stoplight}/domain/ports/clock.rbs +6 -0
  138. data/sig/stoplight/domain/ports/data_store.rbs +9 -0
  139. data/sig/stoplight/domain/ports/error_notifier.rbs +12 -0
  140. data/sig/stoplight/domain/ports/exception_matcher.rbs +10 -0
  141. data/sig/{_private/stoplight → stoplight}/domain/ports/metrics_store.rbs +2 -2
  142. data/sig/stoplight/domain/ports/registry.rbs +13 -0
  143. data/sig/{_private/stoplight → stoplight}/domain/ports/run_strategy.rbs +2 -1
  144. data/sig/stoplight/domain/ports/state_transition_notifier.rbs +18 -0
  145. data/sig/stoplight/domain/ports/telemetry.rbs +28 -0
  146. data/sig/stoplight/domain/ports/telemetry_publisher.rbs +14 -0
  147. data/sig/{_private/stoplight → stoplight}/domain/ports/traffic_control.rbs +7 -0
  148. data/sig/{_private/stoplight → stoplight}/domain/ports/traffic_recovery.rbs +2 -3
  149. data/sig/stoplight/domain/strategies/green_run_strategy.rbs +22 -0
  150. data/sig/stoplight/domain/strategies/red_run_strategy.rbs +19 -0
  151. data/sig/{_private/stoplight → stoplight}/domain/strategies/yellow_run_strategy.rbs +23 -12
  152. data/sig/stoplight/domain/telemetry/bus.rbs +49 -0
  153. data/sig/stoplight/domain/telemetry/consumer.rbs +13 -0
  154. data/sig/stoplight/domain/telemetry/emitter.rbs +51 -0
  155. data/sig/stoplight/domain/telemetry/envelope.rbs +30 -0
  156. data/sig/stoplight/domain/telemetry/failure.rbs +23 -0
  157. data/sig/stoplight/domain/telemetry/light_registered.rbs +23 -0
  158. data/sig/stoplight/domain/telemetry/lock_changed.rbs +31 -0
  159. data/sig/stoplight/domain/telemetry/metrics.rbs +27 -0
  160. data/sig/stoplight/domain/telemetry/recovery_failed.rbs +36 -0
  161. data/sig/stoplight/domain/telemetry/recovery_probe_completed.rbs +29 -0
  162. data/sig/stoplight/domain/telemetry/recovery_started.rbs +26 -0
  163. data/sig/stoplight/domain/telemetry/recovery_succeeded.rbs +31 -0
  164. data/sig/stoplight/domain/telemetry/run_completed.rbs +38 -0
  165. data/sig/stoplight/domain/telemetry/run_recorder.rbs +27 -0
  166. data/sig/stoplight/domain/telemetry/settings.rbs +47 -0
  167. data/sig/stoplight/domain/telemetry/state_transitioned.rbs +11 -0
  168. data/sig/stoplight/domain/telemetry/subscription.rbs +9 -0
  169. data/sig/stoplight/domain/telemetry/traffic_breached.rbs +36 -0
  170. data/sig/stoplight/domain/telemetry/types.rbs +32 -0
  171. data/sig/{_private/stoplight → stoplight}/domain/tracker/recovery_probe.rbs +10 -6
  172. data/sig/{_private/stoplight → stoplight}/domain/tracker/request.rbs +3 -3
  173. data/sig/{_private/stoplight → stoplight}/domain/traffic_control/consecutive_errors.rbs +2 -0
  174. data/sig/{_private/stoplight → stoplight}/domain/traffic_control/error_rate.rbs +3 -2
  175. data/sig/{_private/stoplight → stoplight}/domain/traffic_recovery/consecutive_successes.rbs +2 -0
  176. data/sig/stoplight/domain/types.rbs +25 -0
  177. data/sig/stoplight/error.rbs +8 -0
  178. data/sig/stoplight/generators/rails_generators_base.rbs +11 -0
  179. data/sig/stoplight/infrastructure/config_serializer.rbs +12 -0
  180. data/sig/stoplight/infrastructure/fail_safe/error_notifier.rbs +13 -0
  181. data/sig/{_private/stoplight → stoplight}/infrastructure/fail_safe/storage/metrics.rbs +2 -2
  182. data/sig/{_private/stoplight → stoplight}/infrastructure/fail_safe/storage/recovery_lock.rbs +2 -2
  183. data/sig/stoplight/infrastructure/fail_safe/storage/registry.rbs +23 -0
  184. data/sig/{_private/stoplight → stoplight}/infrastructure/fail_safe/storage/state.rbs +4 -4
  185. data/sig/stoplight/infrastructure/memory/storage/registry.rbs +13 -0
  186. data/sig/{_private/stoplight → stoplight}/infrastructure/memory/storage/state.rbs +3 -3
  187. data/sig/{_private/stoplight → stoplight}/infrastructure/memory/storage/unbounded_metrics.rbs +7 -7
  188. data/sig/stoplight/infrastructure/memory/storage/window_metrics/sliding_window.rbs +27 -0
  189. data/sig/{_private/stoplight → stoplight}/infrastructure/memory/storage/window_metrics.rbs +7 -7
  190. data/sig/stoplight/infrastructure/notifier/fail_safe.rbs +15 -0
  191. data/sig/{_private/stoplight → stoplight}/infrastructure/notifier/generic.rbs +1 -1
  192. data/sig/{_private/stoplight → stoplight}/infrastructure/notifier/io.rbs +1 -1
  193. data/sig/{_private/stoplight → stoplight}/infrastructure/notifier/logger.rbs +1 -1
  194. data/sig/stoplight/infrastructure/redis/key.rbs +10 -0
  195. data/sig/{_private/stoplight → stoplight}/infrastructure/redis/storage/recovery_lock.rbs +2 -2
  196. data/sig/stoplight/infrastructure/redis/storage/registry.rbs +27 -0
  197. data/sig/stoplight/infrastructure/redis/storage/scripting.rbs +34 -0
  198. data/sig/{_private/stoplight → stoplight}/infrastructure/redis/storage/state.rbs +4 -4
  199. data/sig/{_private/stoplight → stoplight}/infrastructure/redis/storage/unbounded_metrics.rbs +8 -1
  200. data/sig/stoplight/infrastructure/redis/storage/window_metrics.rbs +35 -0
  201. data/sig/stoplight/infrastructure/types.rbs +6 -0
  202. data/sig/stoplight/notifier.rbs +1 -1
  203. data/sig/stoplight/ports/system.rbs +16 -10
  204. data/sig/stoplight/state.rbs +3 -3
  205. data/sig/stoplight/telemetry.rbs +17 -0
  206. data/sig/stoplight/types.rbs +9 -0
  207. data/sig/{_private/stoplight → stoplight}/wiring/config_compatibility_validator.rbs +2 -0
  208. data/sig/stoplight/wiring/default.rbs +26 -0
  209. data/sig/stoplight/wiring/default_configuration.rbs +39 -0
  210. data/sig/stoplight/wiring/external_caller.rbs +13 -0
  211. data/sig/stoplight/wiring/fail_safe_config.rbs +7 -0
  212. data/sig/stoplight/wiring/global_state.rbs +36 -0
  213. data/sig/{_private/stoplight/wiring/light_factory → stoplight/wiring/light_configuration_dsl}/traffic_control_dsl.rbs +1 -1
  214. data/sig/{_private/stoplight/wiring/light_factory → stoplight/wiring/light_configuration_dsl}/traffic_recovery_dsl.rbs +1 -1
  215. data/sig/stoplight/wiring/light_configuration_dsl.rbs +38 -0
  216. data/sig/stoplight/wiring/light_factory.rbs +78 -0
  217. data/sig/stoplight/wiring/notifier_bridge.rbs +15 -0
  218. data/sig/stoplight/wiring/notifier_factory.rbs +10 -0
  219. data/sig/{_private/stoplight → stoplight}/wiring/redis/backend.rbs +6 -6
  220. data/sig/stoplight/wiring/system/storage.rbs +35 -0
  221. data/sig/stoplight/wiring/system.rbs +30 -0
  222. data/sig/stoplight/wiring/system_configuration_dsl.rbs +39 -0
  223. data/sig/stoplight/wiring/types.rbs +6 -0
  224. data/sig/stoplight.rbs +37 -54
  225. metadata +190 -143
  226. data/lib/stoplight/admin/actions/lock_all_green.rb +0 -18
  227. data/lib/stoplight/admin/actions/lock_green.rb +0 -23
  228. data/lib/stoplight/admin/actions/lock_red.rb +0 -23
  229. data/lib/stoplight/admin/lights_repository/light.rb +0 -171
  230. data/lib/stoplight/admin/lights_repository.rb +0 -87
  231. data/lib/stoplight/domain/light/configuration_builder_interface.rb +0 -236
  232. data/lib/stoplight/infrastructure/fail_safe/data_store.rb +0 -152
  233. data/lib/stoplight/infrastructure/memory/data_store/metrics.rb +0 -27
  234. data/lib/stoplight/infrastructure/memory/data_store/recovery_lock_store.rb +0 -52
  235. data/lib/stoplight/infrastructure/memory/data_store/recovery_lock_token.rb +0 -17
  236. data/lib/stoplight/infrastructure/memory/data_store/sliding_window.rb +0 -74
  237. data/lib/stoplight/infrastructure/memory/data_store/state.rb +0 -21
  238. data/lib/stoplight/infrastructure/memory/data_store.rb +0 -342
  239. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/get_metrics.lua +0 -26
  240. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/record_failure.lua +0 -36
  241. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/record_recovery_probe_failure.lua +0 -27
  242. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/record_recovery_probe_success.lua +0 -23
  243. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/record_success.lua +0 -35
  244. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/release_lock.lua +0 -6
  245. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/transition_to_green.lua +0 -10
  246. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/transition_to_red.lua +0 -10
  247. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/transition_to_yellow.lua +0 -9
  248. data/lib/stoplight/infrastructure/redis/data_store/recovery_lock_store.rb +0 -60
  249. data/lib/stoplight/infrastructure/redis/data_store/recovery_lock_token.rb +0 -28
  250. data/lib/stoplight/infrastructure/redis/data_store/scripting.rb +0 -73
  251. data/lib/stoplight/infrastructure/redis/data_store.rb +0 -516
  252. data/lib/stoplight/infrastructure/redis/storage/key_space.rb +0 -51
  253. data/lib/stoplight/infrastructure/storage/compatibility_metrics.rb +0 -41
  254. data/lib/stoplight/infrastructure/storage/compatibility_recovery_lock.rb +0 -33
  255. data/lib/stoplight/infrastructure/storage/compatibility_recovery_metrics.rb +0 -47
  256. data/lib/stoplight/infrastructure/storage/compatibility_state.rb +0 -44
  257. data/lib/stoplight/wiring/light_builder.rb +0 -198
  258. data/lib/stoplight/wiring/notifier_factory.rb +0 -26
  259. data/lib/stoplight/wiring/system/light_builder.rb +0 -47
  260. data/lib/stoplight/wiring/system/light_factory.rb +0 -64
  261. data/sig/_private/stoplight/domain/light.rbs +0 -25
  262. data/sig/_private/stoplight/domain/ports/data_store.rbs +0 -76
  263. data/sig/_private/stoplight/domain/ports/light_factory.rbs +0 -80
  264. data/sig/_private/stoplight/domain/strategies/green_run_strategy.rbs +0 -17
  265. data/sig/_private/stoplight/domain/strategies/red_run_strategy.rbs +0 -17
  266. data/sig/_private/stoplight/domain/tracker/base.rbs +0 -8
  267. data/sig/_private/stoplight/infrastructure/fail_safe/data_store.rbs +0 -26
  268. data/sig/_private/stoplight/infrastructure/memory/data_store/metrics.rbs +0 -25
  269. data/sig/_private/stoplight/infrastructure/memory/data_store/recovery_lock_store.rbs +0 -19
  270. data/sig/_private/stoplight/infrastructure/memory/data_store/recovery_lock_token.rbs +0 -17
  271. data/sig/_private/stoplight/infrastructure/memory/data_store/sliding_window.rbs +0 -27
  272. data/sig/_private/stoplight/infrastructure/memory/data_store/state.rbs +0 -17
  273. data/sig/_private/stoplight/infrastructure/memory/data_store.rbs +0 -30
  274. data/sig/_private/stoplight/infrastructure/notifier/fail_safe.rbs +0 -17
  275. data/sig/_private/stoplight/infrastructure/redis/data_store/recovery_lock_store.rbs +0 -24
  276. data/sig/_private/stoplight/infrastructure/redis/data_store/recovery_lock_token.rbs +0 -21
  277. data/sig/_private/stoplight/infrastructure/redis/data_store/scripting.rbs +0 -34
  278. data/sig/_private/stoplight/infrastructure/redis/data_store.rbs +0 -67
  279. data/sig/_private/stoplight/infrastructure/redis/storage/key_space.rbs +0 -19
  280. data/sig/_private/stoplight/infrastructure/redis/storage/scripting.rbs +0 -13
  281. data/sig/_private/stoplight/infrastructure/redis/storage/window_metrics.rbs +0 -34
  282. data/sig/_private/stoplight/infrastructure/storage/compatibility_metrics.rbs +0 -17
  283. data/sig/_private/stoplight/infrastructure/storage/compatibility_recovery_lock.rbs +0 -13
  284. data/sig/_private/stoplight/infrastructure/storage/compatibility_recovery_metrics.rbs +0 -14
  285. data/sig/_private/stoplight/infrastructure/storage/compatibility_state.rbs +0 -14
  286. data/sig/_private/stoplight/system/light_builder.rbs +0 -23
  287. data/sig/_private/stoplight/system/light_factory.rbs +0 -17
  288. data/sig/_private/stoplight/types.rbs +0 -6
  289. data/sig/_private/stoplight/wiring/configuration_dsl.rbs +0 -43
  290. data/sig/_private/stoplight/wiring/default.rbs +0 -26
  291. data/sig/_private/stoplight/wiring/default_configuration.rbs +0 -29
  292. data/sig/_private/stoplight/wiring/light_builder.rbs +0 -48
  293. data/sig/_private/stoplight/wiring/light_factory.rbs +0 -16
  294. data/sig/_private/stoplight/wiring/notifier_factory.rbs +0 -10
  295. data/sig/_private/stoplight/wiring/system.rbs +0 -15
  296. data/sig/_private/stoplight.rbs +0 -48
  297. data/sig/stoplight/data_store.rbs +0 -19
  298. data/sig/stoplight/ports/configuration.rbs +0 -19
  299. data/sig/stoplight/ports/exception_matcher.rbs +0 -8
  300. data/sig/stoplight/ports/light.rbs +0 -12
  301. data/sig/stoplight/ports/light_info.rbs +0 -5
  302. data/sig/stoplight/ports/state_transition_notifier.rbs +0 -15
  303. /data/sig/{_private/stoplight → stoplight}/common/deprecations.rbs +0 -0
  304. /data/sig/{_private/stoplight → stoplight}/domain/compatibility_result.rbs +0 -0
  305. /data/sig/{_private/stoplight → stoplight}/domain/metrics_snapshot.rbs +0 -0
  306. /data/sig/{_private/stoplight → stoplight}/domain/ports/recovery_lock_store.rbs +0 -0
  307. /data/sig/{_private/stoplight → stoplight}/domain/ports/recovery_lock_token.rbs +0 -0
  308. /data/sig/{_private/stoplight → stoplight}/domain/ports/state_store.rbs +0 -0
  309. /data/sig/{_private/stoplight → stoplight}/domain/state_snapshot.rbs +0 -0
  310. /data/sig/{_private/stoplight → stoplight}/domain/storage/recovery_lock_token.rbs +0 -0
  311. /data/sig/{_private/stoplight → stoplight}/domain/traffic_recovery.rbs +0 -0
  312. /data/sig/{_private → stoplight}/generators/stoplight/install/install_generator.rbs +0 -0
  313. /data/sig/{_private/stoplight → stoplight}/infrastructure/fail_safe/storage/recovery_lock_token.rbs +0 -0
  314. /data/sig/{_private/stoplight → stoplight}/infrastructure/memory/storage/recovery_lock.rbs +0 -0
  315. /data/sig/{_private/stoplight → stoplight}/infrastructure/memory/storage/recovery_metrics.rbs +0 -0
  316. /data/sig/{_private/stoplight → stoplight}/infrastructure/redis/storage/metrics.rbs +0 -0
  317. /data/sig/{_private/stoplight → stoplight}/infrastructure/redis/storage/recovery_metrics.rbs +0 -0
  318. /data/sig/{_private/stoplight → stoplight}/infrastructure/system_clock.rbs +0 -0
  319. /data/sig/{_private/stoplight → stoplight}/wiring/data_store_backend.rbs +0 -0
  320. /data/sig/{_private/stoplight → stoplight}/wiring/default_config.rbs +0 -0
  321. /data/sig/{_private/stoplight → stoplight}/wiring/memory/backend.rbs +0 -0
  322. /data/sig/{_private/stoplight → stoplight}/wiring/storage_set.rbs +0 -0
  323. /data/sig/{_private/stoplight → stoplight}/wiring/storage_set_builder.rbs +0 -0
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ # In-process telemetry bus
7
+ class Bus
8
+ # Value-level mirror of the +event+ union.
9
+ EVENT_CLASSES = [
10
+ RunCompleted,
11
+ TrafficBreached,
12
+ RecoveryStarted,
13
+ RecoverySucceeded,
14
+ RecoveryFailed,
15
+ LockChanged,
16
+ RecoveryProbeCompleted,
17
+ LightRegistered
18
+ ].freeze
19
+
20
+ MAX_SUBSCRIPTIONS = 1_000
21
+
22
+ def initialize(error_notifier:, max_subscriptions: MAX_SUBSCRIPTIONS)
23
+ @mutex = Mutex.new
24
+ @error_notifier = error_notifier
25
+ @max_subscriptions = max_subscriptions
26
+ @subscriptions = []
27
+ rebuild
28
+ end
29
+
30
+ def subscribe(filter = nil, &handler)
31
+ raise ArgumentError, "nothing to subscribe. Please, pass a block into `Telemetry#subscribe`" unless handler
32
+ raise ArgumentError, "filter must be nil or a Module, got #{filter.inspect}" unless filter.nil? || filter.is_a?(Module)
33
+ unless filter.nil? || EVENT_CLASSES.any? { |event_class| event_class <= filter }
34
+ raise ArgumentError, "filter #{filter.inspect} does not match any telemetry event class"
35
+ end
36
+
37
+ subscription = Subscription.new
38
+ @mutex.synchronize do
39
+ if @subscriptions.size >= @max_subscriptions
40
+ raise Stoplight::Error::TooManySubscriptions, "exceeded #{@max_subscriptions} telemetry subscriptions"
41
+ end
42
+
43
+ @subscriptions << [subscription, filter, handler]
44
+ rebuild
45
+ end
46
+ subscription
47
+ end
48
+
49
+ def unsubscribe(subscription)
50
+ @mutex.synchronize do
51
+ @subscriptions.reject! { |token, _, _| token.equal?(subscription) }
52
+ rebuild
53
+ end
54
+ nil
55
+ end
56
+
57
+ def subscribed?(event_class)
58
+ @dispatch.key?(event_class)
59
+ end
60
+
61
+ def publish(envelope)
62
+ @dispatch[envelope.payload.class]&.each do |handler|
63
+ handler.call(envelope)
64
+ rescue => error
65
+ begin
66
+ @error_notifier.call(error)
67
+ rescue => notifier_error
68
+ warn "Stoplight telemetry error_notifier raised: #{notifier_error.message}"
69
+ end
70
+ end
71
+ nil
72
+ end
73
+
74
+ # Called under @mutex. Resolves every filter (nil = firehose, module, or exact class) against the closed set of
75
+ # concrete classes and swaps in a frozen table - copy-on-write, so publish never sees a table mid-mutation and
76
+ # needs no lock.
77
+ private def rebuild
78
+ table = {} #: Hash[Class, Array[untyped]]
79
+ EVENT_CLASSES.each do |event_class|
80
+ handlers = @subscriptions.filter_map do |_, filter, handler|
81
+ handler if filter.nil? || event_class <= filter
82
+ end
83
+ table[event_class] = handlers.freeze unless handlers.empty?
84
+ end
85
+ @dispatch = table.freeze
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ # Consumer-only view of a telemetry bus - forwards subscribe/unsubscribe, never publish.
7
+ class Consumer
8
+ def initialize(telemetry)
9
+ @telemetry = telemetry
10
+ end
11
+
12
+ def subscribe(...) = @telemetry.subscribe(...)
13
+
14
+ def unsubscribe(...) = @telemetry.unsubscribe(...)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ class Emitter
7
+ def initialize(bus:, clock:, system_name:, light_name:, error_notifier:)
8
+ @bus = bus
9
+ @clock = clock
10
+ @system_name = system_name
11
+ @light_name = light_name
12
+ @error_notifier = error_notifier
13
+ end
14
+
15
+ # True when any subscriber would receive this event class.
16
+ def subscribed?(event_class) = @bus.subscribed?(event_class)
17
+
18
+ # Emits telemetry event. A failure to build or publish it is routed to the error
19
+ # notifier, never to the caller - the same isolation the bus gives subscriber handlers.
20
+ def emit(event_class)
21
+ return unless subscribed?(event_class)
22
+
23
+ @bus.publish(
24
+ Envelope.new(
25
+ light_name: @light_name,
26
+ system_name: @system_name,
27
+ occurred_at: @clock.current_time,
28
+ payload: yield
29
+ )
30
+ )
31
+ rescue => error
32
+ notify_error(error)
33
+ end
34
+
35
+ private
36
+
37
+ def notify_error(error)
38
+ @error_notifier.call(error)
39
+ rescue => notifier_error
40
+ warn "Stoplight telemetry error_notifier raised: #{notifier_error.message}"
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ # Wraps every published event.
7
+ Envelope = Data.define(
8
+ :system_name,
9
+ :light_name,
10
+ :occurred_at,
11
+ :payload
12
+ )
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ # Carries the live exception.
7
+ Failure = Data.define(
8
+ :exception,
9
+ :tracked
10
+ )
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ # Emitted once per (process instance, system, light) when the light's configuration first materializes.
7
+ LightRegistered = Data.define(
8
+ :settings
9
+ )
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ # state_transitioned variant: manual lock override changed.
7
+ LockChanged = Data.define(
8
+ :from_color,
9
+ :to_color,
10
+ :from_state,
11
+ :to_state
12
+ )
13
+
14
+ class LockChanged
15
+ include StateTransitioned
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ # The serializable subset of MetricsSnapshot.
7
+ Metrics = Data.define(
8
+ :successes,
9
+ :errors,
10
+ :consecutive_errors,
11
+ :consecutive_successes
12
+ )
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ # state_transitioned variant: recovery policy sent the light back to red.
7
+ RecoveryFailed = Data.define(
8
+ :from_color,
9
+ :to_color,
10
+ :policy,
11
+ :failure,
12
+ :metrics
13
+ )
14
+
15
+ class RecoveryFailed
16
+ include StateTransitioned
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ # Emitted for every recovery probe (a run executed while yellow, under the recovery lock).
7
+ RecoveryProbeCompleted = Data.define(
8
+ :outcome,
9
+ :duration_ms,
10
+ :failure,
11
+ :progress
12
+ )
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ # state_transitioned variant: cool-off elapsed and the first probe explicitly entered recovery.
7
+ RecoveryStarted = Data.define(
8
+ :from_color,
9
+ :to_color,
10
+ :breached_at
11
+ )
12
+
13
+ class RecoveryStarted
14
+ include StateTransitioned
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ # state_transitioned variant: recovery policy resumed traffic.
7
+ RecoverySucceeded = Data.define(
8
+ :from_color,
9
+ :to_color,
10
+ :policy,
11
+ :metrics
12
+ )
13
+
14
+ class RecoverySucceeded
15
+ include StateTransitioned
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ # Emitted for every Light#run.
7
+ RunCompleted = Data.define(
8
+ :outcome,
9
+ :color,
10
+ :duration_ms,
11
+ :failure,
12
+ :fallback_used,
13
+ :retry_after
14
+ )
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ # Builds and emits RunCompleted events for one light color.
7
+ #
8
+ # @api private
9
+ class RunRecorder
10
+ def initialize(emitter:, color:)
11
+ @emitter = emitter
12
+ @color = color
13
+ end
14
+
15
+ # True when any subscriber would receive a RunCompleted event.
16
+ def subscribed? = @emitter.subscribed?(RunCompleted)
17
+
18
+ def record_success(duration_ms:, error: nil)
19
+ emit(
20
+ outcome: :success,
21
+ duration_ms:,
22
+ failure: error && Failure.new(exception: error, tracked: false),
23
+ fallback_used: false,
24
+ retry_after: nil
25
+ )
26
+ end
27
+
28
+ def record_failure(error, duration_ms:, fallback_used:)
29
+ emit(
30
+ outcome: :failure,
31
+ duration_ms:,
32
+ failure: Failure.new(exception: error, tracked: true),
33
+ fallback_used:,
34
+ retry_after: nil
35
+ )
36
+ end
37
+
38
+ def record_blocked(fallback_used:, retry_after:)
39
+ emit(
40
+ outcome: :blocked,
41
+ duration_ms: nil,
42
+ failure: nil,
43
+ fallback_used:,
44
+ retry_after:
45
+ )
46
+ end
47
+
48
+ private
49
+
50
+ def emit(outcome:, duration_ms:, failure:, fallback_used:, retry_after:)
51
+ @emitter.emit(RunCompleted) do
52
+ RunCompleted.new(outcome:, color: @color, duration_ms:, failure:, fallback_used:, retry_after:)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ # The serializable subset of a light's configuration.
7
+ Settings = Data.define(
8
+ :cool_off_time,
9
+ :threshold,
10
+ :recovery_threshold,
11
+ :window_size,
12
+ :tracked_errors,
13
+ :skipped_errors,
14
+ :traffic_control,
15
+ :traffic_control_params,
16
+ :traffic_recovery,
17
+ :traffic_recovery_params
18
+ )
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ # Runtime name for the +state_transitioned+ union: a marker module included by every transition variant.
7
+ module StateTransitioned
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ # Opaque, single-use token returned by Telemetry#subscribe and accepted by Telemetry#unsubscribe.
7
+ class Subscription
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ module Telemetry
6
+ # state_transitioned variant: traffic control tripped the light.
7
+ TrafficBreached = Data.define(
8
+ :from_color,
9
+ :to_color,
10
+ :policy,
11
+ :failure,
12
+ :metrics
13
+ )
14
+
15
+ class TrafficBreached
16
+ include StateTransitioned
17
+ end
18
+ end
19
+ end
20
+ end
@@ -4,54 +4,98 @@ module Stoplight
4
4
  module Domain
5
5
  module Tracker
6
6
  class RecoveryProbe
7
- def initialize(traffic_recovery:, notifiers:, config:, metrics_store:, state_store:)
7
+ def initialize(traffic_recovery:, config:, metrics_store:, state_store:, emitter:)
8
8
  @traffic_recovery = traffic_recovery
9
- @notifiers = notifiers
10
9
  @config = config
11
10
  @metrics_store = metrics_store
12
11
  @state_store = state_store
12
+ @emitter = emitter
13
13
  end
14
14
 
15
- # @param exception [Exception]
16
- def record_failure(exception)
15
+ def record_failure(exception, duration_ms:)
17
16
  metrics_store.record_failure(exception)
18
17
 
19
- recover
18
+ recover(duration_ms:, exception:)
20
19
  end
21
20
 
22
- def record_success
21
+ def record_success(duration_ms:)
23
22
  metrics_store.record_success
24
23
 
25
- recover
24
+ recover(duration_ms:, exception: nil)
26
25
  end
27
26
 
28
27
  private
29
28
 
30
29
  attr_reader :traffic_recovery
31
- attr_reader :notifiers
32
30
  attr_reader :config
33
31
  attr_reader :metrics_store
34
32
  attr_reader :state_store
33
+ attr_reader :emitter
35
34
 
36
- def recover
35
+ def recover(duration_ms:, exception:)
37
36
  recovery_metrics = metrics_store.metrics_snapshot
37
+
38
+ emitter.emit(Telemetry::RecoveryProbeCompleted) do
39
+ failure = exception ? Telemetry::Failure.new(exception:, tracked: true) : nil
40
+ Telemetry::RecoveryProbeCompleted.new(
41
+ outcome: failure ? :failure : :success,
42
+ duration_ms:,
43
+ failure:,
44
+ progress: Telemetry::Metrics.new(
45
+ successes: recovery_metrics.successes,
46
+ errors: recovery_metrics.errors,
47
+ consecutive_errors: recovery_metrics.consecutive_errors,
48
+ consecutive_successes: recovery_metrics.consecutive_successes
49
+ )
50
+ )
51
+ end
52
+
38
53
  recovery_result = traffic_recovery.determine_color(config, recovery_metrics)
39
54
 
40
55
  return if recovery_result == TrafficRecovery::YELLOW
41
56
 
42
- from_color, to_color = case recovery_result
43
- when TrafficRecovery::GREEN then [Color::YELLOW, Color::GREEN]
44
- when TrafficRecovery::RED then [Color::YELLOW, Color::RED]
57
+ case recovery_result
58
+ when TrafficRecovery::GREEN
59
+ to_color = Color::GREEN
60
+ if transition(to_color:)
61
+ emitter.emit(Telemetry::RecoverySucceeded) do
62
+ Telemetry::RecoverySucceeded.new(from_color: Color::YELLOW, to_color:,
63
+ policy: traffic_recovery.name,
64
+ metrics: Telemetry::Metrics.new(
65
+ successes: recovery_metrics.successes,
66
+ errors: recovery_metrics.errors,
67
+ consecutive_errors: recovery_metrics.consecutive_errors,
68
+ consecutive_successes: recovery_metrics.consecutive_successes
69
+ ))
70
+ end
71
+ end
72
+ when TrafficRecovery::RED
73
+ to_color = Color::RED
74
+ if transition(to_color:)
75
+ emitter.emit(Telemetry::RecoveryFailed) do
76
+ Telemetry::RecoveryFailed.new(
77
+ from_color: Color::YELLOW,
78
+ to_color:,
79
+ policy: traffic_recovery.name,
80
+ failure: exception ? Telemetry::Failure.new(exception:, tracked: true) : nil,
81
+ metrics: Telemetry::Metrics.new(
82
+ successes: recovery_metrics.successes,
83
+ errors: recovery_metrics.errors,
84
+ consecutive_errors: recovery_metrics.consecutive_errors,
85
+ consecutive_successes: recovery_metrics.consecutive_successes
86
+ )
87
+ )
88
+ end
89
+ end
45
90
  else
46
91
  raise "recovery strategy returned unexpected color: #{recovery_result}"
47
92
  end
93
+ end
48
94
 
49
- state_store.transition_to_color(to_color)
95
+ def transition(to_color:)
96
+ return false unless state_store.transition_to_color(to_color)
50
97
  metrics_store.clear
51
- info = LightInfo.new(name: config.name)
52
- notifiers.each do |notifier|
53
- notifier.notify(info, from_color, to_color, nil)
54
- end
98
+ true
55
99
  end
56
100
  end
57
101
  end
@@ -10,17 +10,16 @@ module Stoplight
10
10
  #
11
11
  # @api private
12
12
  class Request
13
- def initialize(traffic_control:, notifiers:, config:, metrics_store:, state_store:)
13
+ def initialize(traffic_control:, config:, metrics_store:, state_store:, emitter:)
14
14
  @traffic_control = traffic_control
15
- @notifiers = notifiers
16
15
  @config = config
17
16
  @metrics_store = metrics_store
18
17
  @state_store = state_store
18
+ @emitter = emitter
19
19
  end
20
20
 
21
21
  def record_failure(exception)
22
- metrics_store.record_failure(exception)
23
- metrics = metrics_store.metrics_snapshot
22
+ metrics = metrics_store.record_failure(exception)
24
23
 
25
24
  transition_to_red(exception, metrics:)
26
25
  end
@@ -30,19 +29,29 @@ module Stoplight
30
29
  private
31
30
 
32
31
  attr_reader :traffic_control
33
- attr_reader :notifiers
34
32
  attr_reader :config
35
33
  attr_reader :metrics_store
36
34
  attr_reader :state_store
35
+ attr_reader :emitter
37
36
 
38
37
  def transition_to_red(exception, metrics:)
39
38
  if traffic_control.stop_traffic?(config, metrics)
40
39
  # Returns true only if not yet in red therefore preventing
41
40
  # duplicate notifications
42
41
  if state_store.transition_to_color(Color::RED)
43
- info = LightInfo.new(name: config.name)
44
- notifiers.each do |notifier|
45
- notifier.notify(info, Color::GREEN, Color::RED, exception)
42
+ emitter.emit(Telemetry::TrafficBreached) do
43
+ Telemetry::TrafficBreached.new(
44
+ from_color: Color::GREEN,
45
+ to_color: Color::RED,
46
+ policy: traffic_control.name,
47
+ failure: Telemetry::Failure.new(exception:, tracked: true),
48
+ metrics: Telemetry::Metrics.new(
49
+ successes: metrics.successes,
50
+ errors: metrics.errors,
51
+ consecutive_errors: metrics.consecutive_errors,
52
+ consecutive_successes: metrics.consecutive_successes
53
+ )
54
+ )
46
55
  end
47
56
  end
48
57
  end
@@ -27,11 +27,13 @@ module Stoplight
27
27
  # Will switch to red only if 5 consecutive failures occur regardless of the time window
28
28
  # @api private
29
29
  class ConsecutiveErrors
30
+ NAME = :consecutive_errors
31
+
30
32
  def check_compatibility(config)
31
- if config.threshold <= 0
32
- CompatibilityResult.incompatible("`threshold` should be bigger than 0")
33
- elsif !config.threshold.is_a?(Integer)
33
+ if !config.threshold.is_a?(Integer)
34
34
  CompatibilityResult.incompatible("`threshold` should be an integer")
35
+ elsif config.threshold <= 0
36
+ CompatibilityResult.incompatible("`threshold` should be bigger than 0")
35
37
  else
36
38
  CompatibilityResult.compatible
37
39
  end
@@ -41,9 +43,17 @@ module Stoplight
41
43
  metrics.consecutive_errors >= config.threshold
42
44
  end
43
45
 
46
+ def name = NAME.to_s
47
+
48
+ def hash = self.class.hash
49
+
44
50
  def ==(other)
45
51
  other.is_a?(self.class)
46
52
  end
53
+
54
+ def eql?(other)
55
+ self == other
56
+ end
47
57
  end
48
58
  end
49
59
  end