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
@@ -6,167 +6,77 @@ module Stoplight
6
6
  module Storage
7
7
  # Distributed storage for time-windowed light metrics using Redis.
8
8
  #
9
- # This class implements sliding window metrics using Redis sorted sets (ZSETs)
10
- # for efficient time-range queries. Events are bucketed by hour to bound memory
11
- # usage and enable automatic expiration via Redis TTLs.
12
- #
13
9
  # == Storage Structure
14
10
  #
15
- # Events are stored in hourly buckets as ZSETs:
16
- # stoplight:{version}:{system}:{light}:window_metrics:success:1696154400
17
- # stoplight:{version}:{system}:{light}:window_metrics:failure:1696154400
18
- #
19
- # Each ZSET member is a unique event ID with its timestamp as the score,
20
- # enabling O(log N) range queries via ZCOUNT.
21
- #
22
- # Metadata (consecutive counters, last error) is stored in a hash:
23
- # stoplight:{version}:{system}:{light}:window_metrics
24
- #
25
- # == Bucket Strategy
26
- #
27
- # Fixed 1-hour buckets provide a balance between:
28
- # - Query efficiency: At most ~25 buckets for a 24-hour window
29
- # - Memory efficiency: Natural expiration without manual cleanup
30
- # - Precision: Sub-bucket accuracy via ZSET scores
31
- #
32
- # == Atomicity
33
- #
34
- # All operations use Lua scripts to ensure atomicity:
35
- # - record_success: Increments counter and updates metadata in one round-trip
36
- # - record_failure: Same, plus stores serialized error details
37
- # - metrics_snapshot: Aggregates across buckets atomically
11
+ # Maintains a running sum and stores per-second counts in a redis Hash. Eviction reads
12
+ # only the out-of-window entries from the sorted set index, subtracts their counts from
13
+ # the running totals, then deletes both the hash fields and the sorted-set members.
38
14
  #
39
15
  class WindowMetrics < Metrics
16
+ METRICS_FIELDS = %w[last_success_at last_error_json consecutive_errors consecutive_successes].freeze
17
+ private_constant :METRICS_FIELDS
18
+
40
19
  def initialize(redis:, scripting:, config:, clock:, key_space:)
41
20
  @clock = clock
42
- @scripting = scripting
43
21
  @redis = redis
44
- @config = config
45
- @key_space = key_space
46
- @metrics_key = key_space.key(:window_metrics)
47
- @window_size = T.must(config.window_size).to_i
22
+ @scripting = scripting
23
+ @metrics_key = key_space.join("window_metrics")
24
+ @ts_index_key = key_space.join("window_metrics", "ts_idx")
25
+ @window_size = T.must(config.window_size)
48
26
  end
49
27
 
50
- # Get metrics for the current light
51
- #
52
- # @return [Stoplight::Domain::Metrics]
53
28
  def metrics_snapshot
54
- window_end_ts = clock.current_time.to_f
55
- window_start_ts = window_end_ts - @window_size
56
- failure_keys = failure_bucket_keys(window_end_ts)
57
- success_keys = success_bucket_keys(window_end_ts)
58
-
59
- successes, errors, last_success_at, last_error_json, consecutive_errors, consecutive_successes = scripting.call(
60
- :"window_metrics/metrics_snapshot",
61
- args: [
62
- failure_keys.count,
63
- window_start_ts,
64
- window_end_ts,
65
- "last_success_at", "last_error_json", "consecutive_errors", "consecutive_successes"
66
- ],
67
- keys: [
68
- metrics_key,
69
- *success_keys,
70
- *failure_keys
71
- ]
72
- )
73
- Domain::MetricsSnapshot.new(
74
- successes:, errors:,
75
- consecutive_errors: [consecutive_errors.to_i, errors].min,
76
- consecutive_successes: [consecutive_successes.to_i, successes].min,
77
- last_error: deserialize_failure(last_error_json),
78
- last_success_at: (clock.at(last_success_at.to_f) if last_success_at)
79
- )
29
+ successes, errors, last_success_at, last_error_json, consecutive_errors, consecutive_successes =
30
+ @scripting.call(
31
+ "window_metrics/metrics_snapshot",
32
+ args: [@window_size, *METRICS_FIELDS],
33
+ keys: [@metrics_key, @ts_index_key]
34
+ )
35
+
36
+ build_metrics_snapshot(successes:, errors:, consecutive_errors:, consecutive_successes:,
37
+ last_error_json:, last_success_at:)
80
38
  end
81
39
 
82
- # Records successful circuit breaker execution
83
- #
84
- # @return [void]
85
40
  def record_success
86
- timestamp = clock.current_time.to_f
87
-
88
- scripting.call(
89
- :"window_metrics/record_success",
90
- args: [timestamp, SecureRandom.hex(12), bucket_ttl, metrics_ttl],
91
- keys: [
92
- metrics_key,
93
- successes_key(time: timestamp)
94
- ]
41
+ @scripting.call(
42
+ "window_metrics/record_success",
43
+ args: [@window_size, metrics_ttl],
44
+ keys: [@metrics_key, @ts_index_key]
95
45
  )
96
46
  end
97
47
 
98
- # Records failed circuit breaker execution
99
- #
100
- # @param exception [StandardError]
101
- # @return [void]
102
48
  def record_failure(exception)
103
- timestamp = clock.current_time.to_f
104
-
105
- scripting.call(
106
- :"window_metrics/record_failure",
107
- args: [timestamp, SecureRandom.hex(12), serialize_exception(exception, timestamp:), bucket_ttl, metrics_ttl],
108
- keys: [metrics_key, errors_key(time: timestamp)]
109
- )
110
- end
111
-
112
- def clear
113
- window_end_ts = clock.current_time.to_f
114
- failure_keys = failure_bucket_keys(window_end_ts)
115
- success_keys = success_bucket_keys(window_end_ts)
49
+ timestamp = @clock.current_time.to_f
116
50
 
117
- redis.with do |client|
118
- client.del(metrics_key, *failure_keys, *success_keys)
119
- end
120
- end
51
+ successes, errors, last_success_at, last_error_json, consecutive_errors, consecutive_successes =
52
+ @scripting.call(
53
+ "window_metrics/record_failure",
54
+ args: [serialize_exception(exception, timestamp:), @window_size, metrics_ttl],
55
+ keys: [@metrics_key, @ts_index_key]
56
+ )
121
57
 
122
- # Generates a Redis key for a specific metric and time.
123
- #
124
- # @param metric [Symbol] The metric type (e.g., "errors").
125
- # @param time [Time, Numeric] The time for which to generate the key.
126
- # @return [String] The generated Redis key.
127
- def bucket_key(metric:, time:)
128
- key_space.key(:window_metrics, metric, (time.to_i / bucket_size) * bucket_size)
58
+ build_metrics_snapshot(successes:, errors:, consecutive_errors:, consecutive_successes:,
59
+ last_error_json:, last_success_at:)
129
60
  end
130
61
 
131
- # Retrieves the list of Redis bucket keys required to cover a specific time window.
132
- #
133
- # @param metric The metric type (e.g., "errors").
134
- # @param window_end The end time of the window (can be a Time object or a numeric timestamp).
135
- # @return A list of Redis keys for the buckets that cover the time window.
136
- # @api private
137
- def buckets_for_window(metric:, window_end:)
138
- window_end_ts = window_end.to_i
139
- window_start_ts = window_end_ts - @window_size
140
-
141
- # Find bucket timestamps that contain any part of the window
142
- start_bucket = (window_start_ts / bucket_size) * bucket_size
143
-
144
- # End bucket is the last bucket that contains data within our window
145
- end_bucket = ((window_end_ts - 1) / bucket_size) * bucket_size
146
-
147
- (start_bucket..end_bucket).step(bucket_size).map do |bucket_start|
148
- bucket_key(metric: metric, time: bucket_start)
62
+ def clear
63
+ @redis.with do |client|
64
+ client.del(@metrics_key, @ts_index_key)
149
65
  end
150
66
  end
151
67
 
152
68
  private
153
69
 
154
- attr_reader :redis
155
- attr_reader :scripting
156
- attr_reader :metrics_key
157
- attr_reader :clock
158
- attr_reader :key_space
159
-
160
- def bucket_size = 3600 # 1 hour
161
- def bucket_ttl = @window_size + bucket_size
162
-
163
- def successes_key(time:) = bucket_key(metric: :success, time:)
164
-
165
- def errors_key(time:) = bucket_key(metric: :failure, time:)
166
-
167
- def failure_bucket_keys(window_end) = buckets_for_window(metric: :failure, window_end:)
168
-
169
- def success_bucket_keys(window_end) = buckets_for_window(metric: :success, window_end:)
70
+ def build_metrics_snapshot(successes:, errors:, consecutive_errors:, consecutive_successes:,
71
+ last_error_json:, last_success_at:)
72
+ Domain::MetricsSnapshot.new(
73
+ successes:, errors:,
74
+ consecutive_errors: [consecutive_errors.to_i, errors].min,
75
+ consecutive_successes: [consecutive_successes.to_i, successes].min,
76
+ last_error: deserialize_failure(last_error_json),
77
+ last_success_at: (@clock.at(last_success_at.to_f) if last_success_at)
78
+ )
79
+ end
170
80
  end
171
81
  end
172
82
  end
@@ -10,6 +10,10 @@ module Stoplight
10
10
  class SystemClock
11
11
  def current_time = Time.now.utc
12
12
 
13
+ def monotonic_millis = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
14
+
15
+ def monotonic_seconds = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second)
16
+
13
17
  def at(timestamp) = Time.at(timestamp).utc
14
18
  end
15
19
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ # Public face of the telemetry bus. The bus and its events live in the domain layer
5
+ # (+Domain::Telemetry+); this namespace re-exports them so callers never reference the
6
+ # +Domain::+ boundary - mirroring +Stoplight::Notifier+ and +Stoplight::DataStore+.
7
+ module Telemetry
8
+ RunCompleted = Domain::Telemetry::RunCompleted
9
+ TrafficBreached = Domain::Telemetry::TrafficBreached
10
+ RecoveryStarted = Domain::Telemetry::RecoveryStarted
11
+ RecoverySucceeded = Domain::Telemetry::RecoverySucceeded
12
+ RecoveryFailed = Domain::Telemetry::RecoveryFailed
13
+ LockChanged = Domain::Telemetry::LockChanged
14
+ RecoveryProbeCompleted = Domain::Telemetry::RecoveryProbeCompleted
15
+ LightRegistered = Domain::Telemetry::LightRegistered
16
+
17
+ StateTransitioned = Domain::Telemetry::StateTransitioned
18
+ end
19
+ end
@@ -4,7 +4,19 @@ require "singleton"
4
4
 
5
5
  module Stoplight
6
6
  module Types
7
- def self.undefined = Undefined.instance
7
+ UNDEFINED = Undefined.instance
8
+ def self.undefined = UNDEFINED
9
+
10
+ def self.absurd(value = nil)
11
+ msg = "Control flow reached T.absurd."
12
+
13
+ case value
14
+ when Kernel
15
+ msg += " Got value: #{value}"
16
+ end
17
+
18
+ TypeError.new(msg)
19
+ end
8
20
 
9
21
  # Asserts a value is non-nil, returning it with a narrowed type.
10
22
  #
@@ -19,7 +31,8 @@ module Stoplight
19
31
  # @return [T] the non-nil value
20
32
  #
21
33
  def self.must(value)
22
- if value.nil?
34
+ case value
35
+ when nil
23
36
  raise TypeError, "must not have nil value"
24
37
  else
25
38
  value
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stoplight
4
- VERSION = Gem::Version.new("5.8.3")
4
+ VERSION = Gem::Version.new("6.0.0.rc2")
5
5
  end
@@ -23,18 +23,41 @@ module Stoplight
23
23
  end
24
24
 
25
25
  def call
26
+ validate_window_size!
27
+ validate_cool_off_time!
26
28
  validate_traffic_control!
27
29
  validate_traffic_recovery!
28
30
  config
29
31
  end
30
32
 
33
+ # A window shorter than one bucket cannot be measured, and a fractional one silently
34
+ # rounds to a different span than requested.
35
+ private def validate_window_size!
36
+ window_size = config.window_size
37
+ return if window_size.nil?
38
+ return if window_size.is_a?(Integer) && window_size >= 1
39
+
40
+ raise Error::ConfigurationError,
41
+ "`window_size` should be a whole number of seconds, at least 1, got #{window_size.inspect}",
42
+ ExternalCaller.backtrace
43
+ end
44
+
45
+ private def validate_cool_off_time!
46
+ cool_off_time = config.cool_off_time
47
+ return if cool_off_time.is_a?(Integer) && cool_off_time >= 1
48
+
49
+ raise Error::ConfigurationError,
50
+ "`cool_off_time` should be a whole number of seconds, at least 1, got #{cool_off_time.inspect}",
51
+ ExternalCaller.backtrace
52
+ end
53
+
31
54
  private def validate_traffic_control!
32
55
  traffic_control = config.traffic_control
33
56
  traffic_control.check_compatibility(config).then do |compatibility_result|
34
57
  if compatibility_result.incompatible?
35
58
  raise Error::ConfigurationError,
36
59
  "#{traffic_control} incompatible with config: #{compatibility_result.error_messages}",
37
- caller(8)
60
+ ExternalCaller.backtrace
38
61
  end
39
62
  end
40
63
  end
@@ -45,7 +68,7 @@ module Stoplight
45
68
  if compatibility_result.incompatible?
46
69
  raise Error::ConfigurationError,
47
70
  "#{traffic_recovery} incompatible with config: #{compatibility_result.error_messages}",
48
- caller(8)
71
+ ExternalCaller.backtrace
49
72
  end
50
73
  end
51
74
  end
@@ -3,7 +3,7 @@
3
3
  module Stoplight
4
4
  module Wiring
5
5
  module Default
6
- COOL_OFF_TIME = 60.0
6
+ COOL_OFF_TIME = 60
7
7
 
8
8
  DATA_STORE = Stoplight::DataStore::Memory.new
9
9
 
@@ -4,7 +4,8 @@ module Stoplight
4
4
  module Wiring
5
5
  # Provides default settings for the Stoplight library.
6
6
  DefaultConfig = Domain::Config.new(
7
- name: "DEFAULT_CONFIG",
7
+ id: Domain::Id.for("Default"),
8
+ name: "Default",
8
9
  cool_off_time: Default::COOL_OFF_TIME,
9
10
  threshold: Default::THRESHOLD,
10
11
  recovery_threshold: Default::RECOVERY_THRESHOLD,
@@ -46,7 +46,7 @@ module Stoplight
46
46
  #
47
47
  class DefaultConfiguration
48
48
  def initialize
49
- @config = DefaultConfig.with
49
+ @config = DefaultConfig
50
50
  @cool_off_time = T.undefined
51
51
  @threshold = T.undefined
52
52
  @recovery_threshold = T.undefined
@@ -76,7 +76,8 @@ module Stoplight
76
76
 
77
77
  # Builds and validates configuration
78
78
  def to_config!
79
- ConfigurationDsl.new(
79
+ SystemConfigurationDsl.new(
80
+ "Default",
80
81
  cool_off_time: @cool_off_time,
81
82
  threshold: @threshold,
82
83
  recovery_threshold: @recovery_threshold,
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Wiring
5
+ # Locates the application code that called into Stoplight, so configuration errors point at
6
+ # the light's call site instead of Stoplight's own internals.
7
+ #
8
+ # @api private
9
+ module ExternalCaller
10
+ LIB_ROOT = "#{File.expand_path("../../..", __FILE__)}/"
11
+
12
+ class << self
13
+ def backtrace
14
+ frames = caller_locations(1) || []
15
+ external = frames.reject { |frame| internal?(frame) }
16
+ external = frames if external.empty?
17
+ external.map(&:to_s)
18
+ end
19
+
20
+ private def internal?(frame)
21
+ path = frame.path.to_s
22
+ path.start_with?(LIB_ROOT, "<internal:")
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Wiring
5
+ # Empty notifiers prevent notification loops; inherited Memory data store prevents infinite recursion if Redis is down.
6
+ FailSafeConfig = DefaultConfig.with(
7
+ notifiers: [],
8
+ data_store: DataStore::Memory.new
9
+ )
10
+ end
11
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Wiring
5
+ class GlobalState
6
+ attr_reader :default_system
7
+ attr_reader :default_config
8
+
9
+ def initialize(default_config:)
10
+ @default_config = default_config
11
+ @systems = Concurrent::Map.new
12
+ @default_system = register_system_with_config(@default_config)
13
+ end
14
+
15
+ def register_system(
16
+ name,
17
+ cool_off_time: T.undefined,
18
+ threshold: T.undefined,
19
+ recovery_threshold: T.undefined,
20
+ window_size: T.undefined,
21
+ tracked_errors: T.undefined,
22
+ skipped_errors: T.undefined,
23
+ data_store: T.undefined,
24
+ error_notifier: T.undefined,
25
+ notifiers: T.undefined,
26
+ traffic_control: T.undefined,
27
+ traffic_recovery: T.undefined
28
+ )
29
+ register_system_with_config(
30
+ Wiring::SystemConfigurationDsl.new(
31
+ name,
32
+ cool_off_time:,
33
+ threshold:,
34
+ recovery_threshold:,
35
+ window_size:,
36
+ tracked_errors:,
37
+ skipped_errors:,
38
+ traffic_control:,
39
+ traffic_recovery:,
40
+ data_store:,
41
+ error_notifier:,
42
+ notifiers:
43
+ ).configure!(@default_config)
44
+ )
45
+ end
46
+
47
+ private
48
+
49
+ def register_system_with_config(config)
50
+ @systems.compute(config.name) do |existing_system|
51
+ if existing_system
52
+ raise ArgumentError, "system `#{config.name}` is already in use"
53
+ else
54
+ failover_system = Wiring::System.new(
55
+ config: Wiring::FailSafeConfig.with(
56
+ name: "__stoplight__:failover:#{config.name}",
57
+ data_store: DataStore::Memory.new
58
+ ),
59
+ failover_system: nil,
60
+ registry: Infrastructure::Memory::Storage::Registry.new
61
+ )
62
+ Wiring::System.new(
63
+ config: config,
64
+ failover_system: failover_system,
65
+ registry: create_registry(config, failover_system)
66
+ )
67
+ end
68
+ end
69
+ end
70
+
71
+ def create_registry(config, failover_system)
72
+ case config.data_store
73
+ when DataStore::Redis
74
+ Infrastructure::FailSafe::Storage::Registry.new(
75
+ primary_registry: Infrastructure::Redis::Storage::Registry.new(
76
+ redis: config.data_store.redis,
77
+ key_space: config.data_store.key_space.join(config.id),
78
+ clock: Infrastructure::SystemClock.new,
79
+ config_serializer: Infrastructure::ConfigSerializer
80
+ ),
81
+ error_notifier: config.error_notifier,
82
+ failover_registry: Infrastructure::Memory::Storage::Registry.new,
83
+ circuit_breaker: failover_system.register("registry")
84
+ )
85
+ when DataStore::Memory
86
+ Infrastructure::Memory::Storage::Registry.new
87
+ else
88
+ raise TypeError, "unsupported data store type"
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -2,17 +2,15 @@
2
2
 
3
3
  module Stoplight
4
4
  module Wiring
5
- class LightFactory
5
+ class LightConfigurationDsl
6
6
  TrafficControlDsl = ->(value) {
7
7
  case value
8
8
  in _ if value.respond_to?(:stop_traffic?) # TODO: can be removed in 6.0
9
9
  value
10
- in :consecutive_errors
10
+ in Domain::TrafficControl::ConsecutiveErrors::NAME
11
11
  Domain::TrafficControl::ConsecutiveErrors.new
12
- in :error_rate
12
+ in Domain::TrafficControl::ErrorRate::NAME
13
13
  Domain::TrafficControl::ErrorRate.new
14
- in {error_rate: error_rate_settings}
15
- Domain::TrafficControl::ErrorRate.new(**error_rate_settings)
16
14
  else
17
15
  raise Stoplight::Error::ConfigurationError, <<~ERROR
18
16
  unsupported traffic_control strategy provided (`#{value}`). Supported options:
@@ -2,12 +2,12 @@
2
2
 
3
3
  module Stoplight
4
4
  module Wiring
5
- class LightFactory
5
+ class LightConfigurationDsl
6
6
  TrafficRecoveryDsl = ->(value) {
7
7
  case value
8
8
  in _ if value.respond_to?(:determine_color) # TODO: remove in 6.0
9
9
  value
10
- in :consecutive_successes
10
+ in Domain::TrafficRecovery::ConsecutiveSuccesses::NAME
11
11
  Domain::TrafficRecovery::ConsecutiveSuccesses.new
12
12
  else
13
13
  raise Error::ConfigurationError, <<~ERROR
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Wiring
5
+ class LightConfigurationDsl
6
+ def initialize(
7
+ name:,
8
+ cool_off_time: T.undefined,
9
+ threshold: T.undefined,
10
+ recovery_threshold: T.undefined,
11
+ window_size: T.undefined,
12
+ tracked_errors: T.undefined,
13
+ skipped_errors: T.undefined,
14
+ traffic_control: T.undefined,
15
+ traffic_recovery: T.undefined
16
+ )
17
+ @name = name.to_s
18
+ @id = Domain::Id.for(@name)
19
+ @cool_off_time = cool_off_time
20
+ @threshold = threshold
21
+ @recovery_threshold = recovery_threshold
22
+ @window_size = window_size
23
+ @tracked_errors = if tracked_errors.is_a?(Undefined)
24
+ tracked_errors
25
+ elsif tracked_errors.is_a?(Array)
26
+ tracked_errors
27
+ else
28
+ [tracked_errors]
29
+ end
30
+ @skipped_errors = if skipped_errors.is_a?(Undefined)
31
+ skipped_errors
32
+ elsif skipped_errors.is_a?(Array)
33
+ skipped_errors
34
+ else
35
+ [skipped_errors]
36
+ end
37
+ @traffic_control = traffic_control.is_a?(Undefined) ? traffic_control : TrafficControlDsl.call(traffic_control)
38
+ @traffic_recovery = traffic_recovery.is_a?(Undefined) ? traffic_recovery : TrafficRecoveryDsl.call(traffic_recovery)
39
+
40
+ @digest = calculate_digest
41
+ end
42
+
43
+ def configure!(default_config)
44
+ ConfigCompatibilityValidator.call(
45
+ config: default_config.with(
46
+ id: @id,
47
+ name:,
48
+ cool_off_time:,
49
+ threshold:,
50
+ recovery_threshold:,
51
+ window_size:,
52
+ tracked_errors:,
53
+ skipped_errors:,
54
+ traffic_control:,
55
+ traffic_recovery:
56
+ )
57
+ )
58
+ end
59
+
60
+ attr_reader :digest
61
+
62
+ private
63
+
64
+ def calculate_digest
65
+ [
66
+ @id,
67
+ @cool_off_time,
68
+ @threshold,
69
+ @recovery_threshold,
70
+ @window_size,
71
+ errors_for_digest(@tracked_errors),
72
+ errors_for_digest(@skipped_errors),
73
+ @traffic_control,
74
+ @traffic_recovery
75
+ ].hash
76
+ end
77
+
78
+ attr_reader :name
79
+ attr_reader :cool_off_time
80
+ attr_reader :threshold
81
+ attr_reader :recovery_threshold
82
+ attr_reader :window_size
83
+ attr_reader :tracked_errors
84
+ attr_reader :skipped_errors
85
+ attr_reader :traffic_control
86
+ attr_reader :traffic_recovery
87
+
88
+ def errors_for_digest(errors)
89
+ if errors.is_a?(Undefined)
90
+ errors
91
+ else
92
+ errors.map { |matcher| Domain::MatcherValidator.call(matcher) }
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end