flapjack 1.6.0 → 2.0.0b1

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 (301) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -6
  3. data/.gitmodules +1 -1
  4. data/.rspec +1 -1
  5. data/.ruby-version +1 -1
  6. data/.travis.yml +12 -13
  7. data/CHANGELOG.md +2 -9
  8. data/CONTRIBUTING.md +7 -2
  9. data/Gemfile +4 -13
  10. data/LICENCE +1 -0
  11. data/README.md +8 -2
  12. data/Rakefile +2 -2
  13. data/bin/flapjack +3 -12
  14. data/build.sh +4 -2
  15. data/etc/flapjack_config.toml.example +273 -0
  16. data/features/ack_after_sched_maint.feature +18 -21
  17. data/features/cli.feature +11 -71
  18. data/features/cli_flapjack-feed-events.feature +14 -15
  19. data/features/cli_flapjack-nagios-receiver.feature +12 -41
  20. data/features/cli_flapper.feature +12 -41
  21. data/features/cli_purge.feature +5 -6
  22. data/features/cli_receive-events.feature +6 -7
  23. data/features/cli_simulate-failed-check.feature +5 -6
  24. data/features/events.feature +206 -181
  25. data/features/events_check_names.feature +4 -7
  26. data/features/notification_rules.feature +144 -223
  27. data/features/notifications.feature +65 -57
  28. data/features/rollup.feature +45 -47
  29. data/features/steps/cli_steps.rb +4 -5
  30. data/features/steps/events_steps.rb +163 -373
  31. data/features/steps/notifications_steps.rb +408 -264
  32. data/features/steps/packaging-lintian_steps.rb +0 -4
  33. data/features/steps/time_travel_steps.rb +0 -26
  34. data/features/support/daemons.rb +6 -31
  35. data/features/support/env.rb +65 -74
  36. data/flapjack.gemspec +22 -24
  37. data/lib/flapjack.rb +14 -7
  38. data/lib/flapjack/cli/flapper.rb +74 -173
  39. data/lib/flapjack/cli/maintenance.rb +278 -109
  40. data/lib/flapjack/cli/migrate.rb +950 -0
  41. data/lib/flapjack/cli/purge.rb +19 -22
  42. data/lib/flapjack/cli/receiver.rb +150 -326
  43. data/lib/flapjack/cli/server.rb +8 -235
  44. data/lib/flapjack/cli/simulate.rb +42 -57
  45. data/lib/flapjack/configuration.rb +51 -37
  46. data/lib/flapjack/coordinator.rb +138 -129
  47. data/lib/flapjack/data/acknowledgement.rb +177 -0
  48. data/lib/flapjack/data/alert.rb +97 -158
  49. data/lib/flapjack/data/check.rb +611 -0
  50. data/lib/flapjack/data/condition.rb +70 -0
  51. data/lib/flapjack/data/contact.rb +226 -456
  52. data/lib/flapjack/data/event.rb +96 -184
  53. data/lib/flapjack/data/extensions/associations.rb +59 -0
  54. data/lib/flapjack/data/extensions/short_name.rb +25 -0
  55. data/lib/flapjack/data/medium.rb +428 -0
  56. data/lib/flapjack/data/metrics.rb +194 -0
  57. data/lib/flapjack/data/notification.rb +22 -281
  58. data/lib/flapjack/data/rule.rb +473 -0
  59. data/lib/flapjack/data/scheduled_maintenance.rb +244 -0
  60. data/lib/flapjack/data/state.rb +221 -0
  61. data/lib/flapjack/data/statistic.rb +112 -0
  62. data/lib/flapjack/data/tag.rb +277 -0
  63. data/lib/flapjack/data/test_notification.rb +182 -0
  64. data/lib/flapjack/data/unscheduled_maintenance.rb +159 -0
  65. data/lib/flapjack/data/validators/id_validator.rb +20 -0
  66. data/lib/flapjack/exceptions.rb +6 -0
  67. data/lib/flapjack/filters/acknowledgement.rb +23 -16
  68. data/lib/flapjack/filters/base.rb +0 -5
  69. data/lib/flapjack/filters/delays.rb +53 -43
  70. data/lib/flapjack/filters/ok.rb +23 -14
  71. data/lib/flapjack/filters/scheduled_maintenance.rb +3 -3
  72. data/lib/flapjack/filters/unscheduled_maintenance.rb +12 -3
  73. data/lib/flapjack/gateways/aws_sns.rb +65 -49
  74. data/lib/flapjack/gateways/aws_sns/alert.text.erb +2 -2
  75. data/lib/flapjack/gateways/aws_sns/alert_subject.text.erb +2 -2
  76. data/lib/flapjack/gateways/aws_sns/rollup_subject.text.erb +1 -1
  77. data/lib/flapjack/gateways/email.rb +107 -90
  78. data/lib/flapjack/gateways/email/alert.html.erb +19 -18
  79. data/lib/flapjack/gateways/email/alert.text.erb +20 -14
  80. data/lib/flapjack/gateways/email/alert_subject.text.erb +2 -1
  81. data/lib/flapjack/gateways/email/rollup.html.erb +14 -13
  82. data/lib/flapjack/gateways/email/rollup.text.erb +13 -10
  83. data/lib/flapjack/gateways/jabber.rb +679 -671
  84. data/lib/flapjack/gateways/jabber/alert.text.erb +9 -6
  85. data/lib/flapjack/gateways/jsonapi.rb +164 -350
  86. data/lib/flapjack/gateways/jsonapi/data/join_descriptor.rb +44 -0
  87. data/lib/flapjack/gateways/jsonapi/data/method_descriptor.rb +21 -0
  88. data/lib/flapjack/gateways/jsonapi/helpers/headers.rb +63 -0
  89. data/lib/flapjack/gateways/jsonapi/helpers/miscellaneous.rb +136 -0
  90. data/lib/flapjack/gateways/jsonapi/helpers/resources.rb +227 -0
  91. data/lib/flapjack/gateways/jsonapi/helpers/serialiser.rb +313 -0
  92. data/lib/flapjack/gateways/jsonapi/helpers/swagger_docs.rb +322 -0
  93. data/lib/flapjack/gateways/jsonapi/methods/association_delete.rb +115 -0
  94. data/lib/flapjack/gateways/jsonapi/methods/association_get.rb +288 -0
  95. data/lib/flapjack/gateways/jsonapi/methods/association_patch.rb +178 -0
  96. data/lib/flapjack/gateways/jsonapi/methods/association_post.rb +116 -0
  97. data/lib/flapjack/gateways/jsonapi/methods/metrics.rb +71 -0
  98. data/lib/flapjack/gateways/jsonapi/methods/resource_delete.rb +119 -0
  99. data/lib/flapjack/gateways/jsonapi/methods/resource_get.rb +186 -0
  100. data/lib/flapjack/gateways/jsonapi/methods/resource_patch.rb +239 -0
  101. data/lib/flapjack/gateways/jsonapi/methods/resource_post.rb +197 -0
  102. data/lib/flapjack/gateways/jsonapi/middleware/array_param_fixer.rb +27 -0
  103. data/lib/flapjack/gateways/jsonapi/{rack → middleware}/json_params_parser.rb +7 -6
  104. data/lib/flapjack/gateways/jsonapi/middleware/request_timestamp.rb +18 -0
  105. data/lib/flapjack/gateways/oobetet.rb +222 -170
  106. data/lib/flapjack/gateways/pager_duty.rb +388 -0
  107. data/lib/flapjack/gateways/pager_duty/alert.text.erb +13 -0
  108. data/lib/flapjack/gateways/slack.rb +56 -48
  109. data/lib/flapjack/gateways/slack/alert.text.erb +1 -1
  110. data/lib/flapjack/gateways/slack/rollup.text.erb +1 -1
  111. data/lib/flapjack/gateways/sms_aspsms.rb +155 -0
  112. data/lib/flapjack/gateways/sms_aspsms/alert.text.erb +7 -0
  113. data/lib/flapjack/gateways/sms_aspsms/rollup.text.erb +2 -0
  114. data/lib/flapjack/gateways/sms_messagenet.rb +77 -57
  115. data/lib/flapjack/gateways/sms_messagenet/alert.text.erb +3 -2
  116. data/lib/flapjack/gateways/sms_nexmo.rb +53 -51
  117. data/lib/flapjack/gateways/sms_nexmo/alert.text.erb +2 -2
  118. data/lib/flapjack/gateways/sms_nexmo/rollup.text.erb +1 -1
  119. data/lib/flapjack/gateways/sms_twilio.rb +79 -62
  120. data/lib/flapjack/gateways/sms_twilio/alert.text.erb +3 -2
  121. data/lib/flapjack/gateways/web.rb +437 -345
  122. data/lib/flapjack/gateways/web/middleware/request_timestamp.rb +18 -0
  123. data/lib/flapjack/gateways/web/public/css/bootstrap.css +3793 -4340
  124. data/lib/flapjack/gateways/web/public/css/bootstrap.css.map +1 -0
  125. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.eot +0 -0
  126. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.svg +273 -214
  127. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  128. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.woff +0 -0
  129. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
  130. data/lib/flapjack/gateways/web/public/js/bootstrap.js +1637 -1607
  131. data/lib/flapjack/gateways/web/public/js/self_stats.js +1 -2
  132. data/lib/flapjack/gateways/web/views/_pagination.html.erb +19 -0
  133. data/lib/flapjack/gateways/web/views/check.html.erb +159 -121
  134. data/lib/flapjack/gateways/web/views/checks.html.erb +82 -41
  135. data/lib/flapjack/gateways/web/views/contact.html.erb +59 -71
  136. data/lib/flapjack/gateways/web/views/contacts.html.erb +32 -8
  137. data/lib/flapjack/gateways/web/views/index.html.erb +2 -2
  138. data/lib/flapjack/gateways/web/views/{layout.erb → layout.html.erb} +7 -23
  139. data/lib/flapjack/gateways/web/views/self_stats.html.erb +32 -33
  140. data/lib/flapjack/gateways/web/views/tag.html.erb +32 -0
  141. data/lib/flapjack/gateways/web/views/tags.html.erb +51 -0
  142. data/lib/flapjack/logger.rb +34 -3
  143. data/lib/flapjack/notifier.rb +180 -112
  144. data/lib/flapjack/patches.rb +8 -63
  145. data/lib/flapjack/pikelet.rb +185 -143
  146. data/lib/flapjack/processor.rb +323 -191
  147. data/lib/flapjack/record_queue.rb +33 -0
  148. data/lib/flapjack/redis_proxy.rb +66 -0
  149. data/lib/flapjack/utility.rb +21 -15
  150. data/lib/flapjack/version.rb +2 -1
  151. data/libexec/httpbroker.go +218 -14
  152. data/libexec/oneoff.go +13 -10
  153. data/spec/lib/flapjack/configuration_spec.rb +286 -0
  154. data/spec/lib/flapjack/coordinator_spec.rb +103 -157
  155. data/spec/lib/flapjack/data/check_spec.rb +175 -0
  156. data/spec/lib/flapjack/data/contact_spec.rb +26 -349
  157. data/spec/lib/flapjack/data/event_spec.rb +76 -291
  158. data/spec/lib/flapjack/data/medium_spec.rb +19 -0
  159. data/spec/lib/flapjack/data/rule_spec.rb +43 -0
  160. data/spec/lib/flapjack/data/scheduled_maintenance_spec.rb +976 -0
  161. data/spec/lib/flapjack/data/unscheduled_maintenance_spec.rb +34 -0
  162. data/spec/lib/flapjack/gateways/aws_sns_spec.rb +111 -60
  163. data/spec/lib/flapjack/gateways/email_spec.rb +194 -161
  164. data/spec/lib/flapjack/gateways/jabber_spec.rb +961 -162
  165. data/spec/lib/flapjack/gateways/jsonapi/methods/check_links_spec.rb +155 -0
  166. data/spec/lib/flapjack/gateways/jsonapi/methods/checks_spec.rb +426 -0
  167. data/spec/lib/flapjack/gateways/jsonapi/methods/contact_links_spec.rb +217 -0
  168. data/spec/lib/flapjack/gateways/jsonapi/methods/contacts_spec.rb +425 -0
  169. data/spec/lib/flapjack/gateways/jsonapi/methods/events_spec.rb +271 -0
  170. data/spec/lib/flapjack/gateways/jsonapi/methods/media_spec.rb +257 -0
  171. data/spec/lib/flapjack/gateways/jsonapi/methods/medium_links_spec.rb +163 -0
  172. data/spec/lib/flapjack/gateways/jsonapi/methods/metrics_spec.rb +8 -0
  173. data/spec/lib/flapjack/gateways/jsonapi/methods/rule_links_spec.rb +212 -0
  174. data/spec/lib/flapjack/gateways/jsonapi/methods/rules_spec.rb +289 -0
  175. data/spec/lib/flapjack/gateways/jsonapi/methods/scheduled_maintenance_links_spec.rb +49 -0
  176. data/spec/lib/flapjack/gateways/jsonapi/methods/scheduled_maintenances_spec.rb +242 -0
  177. data/spec/lib/flapjack/gateways/jsonapi/methods/tag_links_spec.rb +274 -0
  178. data/spec/lib/flapjack/gateways/jsonapi/methods/tags_spec.rb +302 -0
  179. data/spec/lib/flapjack/gateways/jsonapi/methods/unscheduled_maintenance_links_spec.rb +49 -0
  180. data/spec/lib/flapjack/gateways/jsonapi/methods/unscheduled_maintenances_spec.rb +339 -0
  181. data/spec/lib/flapjack/gateways/jsonapi_spec.rb +1 -1
  182. data/spec/lib/flapjack/gateways/oobetet_spec.rb +151 -79
  183. data/spec/lib/flapjack/gateways/pager_duty_spec.rb +353 -0
  184. data/spec/lib/flapjack/gateways/slack_spec.rb +53 -53
  185. data/spec/lib/flapjack/gateways/sms_aspsms_spec.rb +106 -0
  186. data/spec/lib/flapjack/gateways/sms_messagenet_spec.rb +111 -54
  187. data/spec/lib/flapjack/gateways/sms_nexmo_spec.rb +50 -51
  188. data/spec/lib/flapjack/gateways/sms_twilio_spec.rb +108 -48
  189. data/spec/lib/flapjack/gateways/web_spec.rb +144 -216
  190. data/spec/lib/flapjack/notifier_spec.rb +132 -1
  191. data/spec/lib/flapjack/pikelet_spec.rb +111 -50
  192. data/spec/lib/flapjack/processor_spec.rb +210 -40
  193. data/spec/lib/flapjack/redis_proxy_spec.rb +45 -0
  194. data/spec/lib/flapjack/utility_spec.rb +11 -15
  195. data/spec/service_consumers/fixture_data.rb +547 -0
  196. data/spec/service_consumers/pact_helper.rb +21 -32
  197. data/spec/service_consumers/pacts/flapjack-diner_v2.0.json +4652 -0
  198. data/spec/service_consumers/provider_states_for_flapjack-diner.rb +279 -322
  199. data/spec/service_consumers/provider_support.rb +8 -0
  200. data/spec/spec_helper.rb +34 -44
  201. data/spec/support/erb_view_helper.rb +1 -1
  202. data/spec/support/factories.rb +58 -0
  203. data/spec/support/jsonapi_helper.rb +15 -26
  204. data/spec/support/mock_logger.rb +43 -0
  205. data/spec/support/xmpp_comparable.rb +24 -0
  206. data/src/flapjack/transport_test.go +30 -1
  207. data/tasks/dump_keys.rake +82 -0
  208. data/tasks/events.rake +7 -7
  209. data/tasks/support/flapjack_config_benchmark.toml +28 -0
  210. data/tasks/support/flapjack_config_benchmark.yaml +0 -2
  211. metadata +175 -222
  212. data/Guardfile +0 -14
  213. data/etc/flapjack_config.yaml.example +0 -477
  214. data/features/cli_flapjack-populator.feature +0 -90
  215. data/features/support/silent_system.rb +0 -4
  216. data/lib/flapjack/cli/import.rb +0 -108
  217. data/lib/flapjack/data/entity.rb +0 -652
  218. data/lib/flapjack/data/entity_check.rb +0 -1044
  219. data/lib/flapjack/data/message.rb +0 -56
  220. data/lib/flapjack/data/migration.rb +0 -234
  221. data/lib/flapjack/data/notification_rule.rb +0 -425
  222. data/lib/flapjack/data/semaphore.rb +0 -44
  223. data/lib/flapjack/data/tagged.rb +0 -48
  224. data/lib/flapjack/gateways/jsonapi/check_methods.rb +0 -206
  225. data/lib/flapjack/gateways/jsonapi/check_presenter.rb +0 -221
  226. data/lib/flapjack/gateways/jsonapi/contact_methods.rb +0 -186
  227. data/lib/flapjack/gateways/jsonapi/entity_methods.rb +0 -223
  228. data/lib/flapjack/gateways/jsonapi/medium_methods.rb +0 -185
  229. data/lib/flapjack/gateways/jsonapi/metrics_methods.rb +0 -132
  230. data/lib/flapjack/gateways/jsonapi/notification_rule_methods.rb +0 -141
  231. data/lib/flapjack/gateways/jsonapi/pagerduty_credential_methods.rb +0 -139
  232. data/lib/flapjack/gateways/jsonapi/report_methods.rb +0 -146
  233. data/lib/flapjack/gateways/pagerduty.rb +0 -318
  234. data/lib/flapjack/gateways/pagerduty/alert.text.erb +0 -10
  235. data/lib/flapjack/gateways/web/public/css/select2-bootstrap.css +0 -87
  236. data/lib/flapjack/gateways/web/public/css/select2.css +0 -615
  237. data/lib/flapjack/gateways/web/public/css/tablesort.css +0 -67
  238. data/lib/flapjack/gateways/web/public/img/select2-spinner.gif +0 -0
  239. data/lib/flapjack/gateways/web/public/img/select2.png +0 -0
  240. data/lib/flapjack/gateways/web/public/img/select2x2.png +0 -0
  241. data/lib/flapjack/gateways/web/public/js/backbone.js +0 -1581
  242. data/lib/flapjack/gateways/web/public/js/backbone.jsonapi.js +0 -322
  243. data/lib/flapjack/gateways/web/public/js/flapjack.js +0 -82
  244. data/lib/flapjack/gateways/web/public/js/jquery.tablesorter.js +0 -1640
  245. data/lib/flapjack/gateways/web/public/js/jquery.tablesorter.widgets.js +0 -1390
  246. data/lib/flapjack/gateways/web/public/js/modules/contact.js +0 -520
  247. data/lib/flapjack/gateways/web/public/js/modules/entity.js +0 -28
  248. data/lib/flapjack/gateways/web/public/js/modules/medium.js +0 -40
  249. data/lib/flapjack/gateways/web/public/js/select2.js +0 -3397
  250. data/lib/flapjack/gateways/web/public/js/tablesort.js +0 -44
  251. data/lib/flapjack/gateways/web/public/js/underscore.js +0 -1276
  252. data/lib/flapjack/gateways/web/views/edit_contacts.html.erb +0 -173
  253. data/lib/flapjack/gateways/web/views/entities.html.erb +0 -30
  254. data/lib/flapjack/gateways/web/views/entity.html.erb +0 -51
  255. data/lib/flapjack/rack_logger.rb +0 -47
  256. data/lib/flapjack/redis_pool.rb +0 -42
  257. data/spec/lib/flapjack/data/entity_check_spec.rb +0 -1418
  258. data/spec/lib/flapjack/data/entity_spec.rb +0 -872
  259. data/spec/lib/flapjack/data/message_spec.rb +0 -30
  260. data/spec/lib/flapjack/data/migration_spec.rb +0 -104
  261. data/spec/lib/flapjack/data/notification_rule_spec.rb +0 -232
  262. data/spec/lib/flapjack/data/notification_spec.rb +0 -53
  263. data/spec/lib/flapjack/data/semaphore_spec.rb +0 -24
  264. data/spec/lib/flapjack/filters/acknowledgement_spec.rb +0 -6
  265. data/spec/lib/flapjack/filters/delays_spec.rb +0 -6
  266. data/spec/lib/flapjack/filters/ok_spec.rb +0 -6
  267. data/spec/lib/flapjack/filters/scheduled_maintenance_spec.rb +0 -6
  268. data/spec/lib/flapjack/filters/unscheduled_maintenance_spec.rb +0 -6
  269. data/spec/lib/flapjack/gateways/jsonapi/check_methods_spec.rb +0 -315
  270. data/spec/lib/flapjack/gateways/jsonapi/check_presenter_spec.rb +0 -223
  271. data/spec/lib/flapjack/gateways/jsonapi/contact_methods_spec.rb +0 -131
  272. data/spec/lib/flapjack/gateways/jsonapi/entity_methods_spec.rb +0 -389
  273. data/spec/lib/flapjack/gateways/jsonapi/medium_methods_spec.rb +0 -231
  274. data/spec/lib/flapjack/gateways/jsonapi/notification_rule_methods_spec.rb +0 -169
  275. data/spec/lib/flapjack/gateways/jsonapi/pagerduty_credential_methods_spec.rb +0 -114
  276. data/spec/lib/flapjack/gateways/jsonapi/report_methods_spec.rb +0 -590
  277. data/spec/lib/flapjack/gateways/pagerduty_spec.rb +0 -249
  278. data/spec/lib/flapjack/gateways/web/views/check.html.erb_spec.rb +0 -21
  279. data/spec/lib/flapjack/gateways/web/views/contact.html.erb_spec.rb +0 -24
  280. data/spec/lib/flapjack/gateways/web/views/index.html.erb_spec.rb +0 -16
  281. data/spec/lib/flapjack/redis_pool_spec.rb +0 -29
  282. data/spec/service_consumers/pacts/flapjack-diner_v1.0.json +0 -4702
  283. data/tasks/entities.rake +0 -151
  284. data/tasks/profile.rake +0 -282
  285. data/tmp/acknowledge.rb +0 -13
  286. data/tmp/create_config_yaml.rb +0 -16
  287. data/tmp/create_event_ok.rb +0 -30
  288. data/tmp/create_event_unknown.rb +0 -30
  289. data/tmp/create_events_failure.rb +0 -34
  290. data/tmp/create_events_ok.rb +0 -32
  291. data/tmp/create_events_ok_fail_ack_ok.rb +0 -53
  292. data/tmp/create_events_ok_failure.rb +0 -41
  293. data/tmp/create_events_ok_failure_ack.rb +0 -53
  294. data/tmp/dummy_contacts.json +0 -43
  295. data/tmp/dummy_entities.json +0 -37
  296. data/tmp/generate_nagios_test_hosts.rb +0 -16
  297. data/tmp/notification_rules.rb +0 -73
  298. data/tmp/parse_config_yaml.rb +0 -7
  299. data/tmp/redis_find_spurious_unknown_states.rb +0 -52
  300. data/tmp/test_json_post.rb +0 -19
  301. data/tmp/test_notification_rules_api.rb +0 -171
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'zermelo/records/redis'
4
+
5
+ module Flapjack
6
+ module Data
7
+ class Condition
8
+
9
+ include Comparable
10
+
11
+ def <=>(cond)
12
+ return nil unless cond.is_a?(Flapjack::Data::Condition)
13
+ self.priority <=> cond.priority
14
+ end
15
+
16
+ # class methods rather than constants, as these may come from config
17
+ # data in the future; name => priority
18
+ def self.healthy
19
+ {
20
+ 'ok' => 1
21
+ }
22
+ end
23
+
24
+ def self.unhealthy
25
+ {
26
+ 'critical' => -3,
27
+ 'warning' => -2,
28
+ 'unknown' => -1
29
+ }
30
+ end
31
+
32
+ # NB: not actually persisted; we probably want a non-persisted record type
33
+ # for this case
34
+ include Zermelo::Records::RedisSet
35
+
36
+ define_attributes :name => :string,
37
+ :priority => :integer
38
+
39
+ validates :name, :presence => true,
40
+ :inclusion => { :in => Flapjack::Data::Condition.healthy.keys +
41
+ Flapjack::Data::Condition.unhealthy.keys }
42
+
43
+ validates :priority, :presence => true,
44
+ :numericality => {:only_integer => true},
45
+ :inclusion => { :in => Flapjack::Data::Condition.healthy.values |
46
+ Flapjack::Data::Condition.unhealthy.values }
47
+
48
+ before_create :save_allowed?
49
+ before_update :save_allowed?
50
+ def save_allowed?
51
+ false
52
+ end
53
+
54
+ def self.healthy?(c)
55
+ self.healthy.keys.include?(c)
56
+ end
57
+
58
+ def self.most_unhealthy
59
+ self.unhealthy.min_by {|_, pri| pri }.first
60
+ end
61
+
62
+ def self.for_name(n)
63
+ c = Flapjack::Data::Condition.new(:name => n,
64
+ :priority => self.healthy[n.to_s] || self.unhealthy[n.to_s] )
65
+ c.valid? ? c : nil
66
+ end
67
+
68
+ end
69
+ end
70
+ end
@@ -1,16 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # NB: use of redis.keys probably indicates we should maintain a data
4
- # structure to avoid the need for this type of query
5
-
6
3
  require 'securerandom'
7
4
  require 'set'
8
5
 
9
6
  require 'ice_cube'
7
+ require 'swagger/blocks'
8
+
9
+ require 'zermelo/records/redis'
10
10
 
11
- require 'flapjack/data/entity'
12
- require 'flapjack/data/entity_check'
13
- require 'flapjack/data/notification_rule'
11
+ require 'flapjack/data/extensions/short_name'
12
+ require 'flapjack/data/validators/id_validator'
13
+
14
+ require 'flapjack/data/extensions/associations'
15
+ require 'flapjack/gateways/jsonapi/data/join_descriptor'
16
+ require 'flapjack/gateways/jsonapi/data/method_descriptor'
14
17
 
15
18
  module Flapjack
16
19
 
@@ -18,515 +21,282 @@ module Flapjack
18
21
 
19
22
  class Contact
20
23
 
21
- attr_accessor :id, :first_name, :last_name, :email, :media,
22
- :media_intervals, :media_rollup_thresholds, :pagerduty_credentials
23
-
24
- ALL_MEDIA = [
25
- 'email',
26
- 'sms',
27
- 'slack',
28
- 'sms_twilio',
29
- 'sms_nexmo',
30
- 'jabber',
31
- 'pagerduty',
32
- 'sns'
33
- ]
34
-
35
- def self.all(options = {})
36
- raise "Redis connection not set" unless redis = options[:redis]
37
-
38
- redis.keys('contact:*').inject([]) {|ret, k|
39
- k =~ /^contact:(.*)$/
40
- id = $1
41
- contact = self.find_by_id(id, :redis => redis)
42
- ret << contact unless contact.nil?
43
- ret
44
- }.sort_by {|c| [c.last_name, c.first_name]}
45
- end
46
-
47
- def self.find_by_id(contact_id, options = {})
48
- raise "Redis connection not set" unless redis = options[:redis]
49
- raise "No id value passed" unless contact_id
50
- logger = options[:logger]
51
-
52
- # sanity check
53
- return unless redis.hexists("contact:#{contact_id}", 'first_name')
54
-
55
- contact = self.new(:id => contact_id, :redis => redis, :logger => logger)
56
- contact.refresh
57
- contact
58
- end
59
-
60
- def self.find_by_ids(contact_ids, options = {})
61
- raise "Redis connection not set" unless redis = options[:redis]
62
- logger = options[:logger]
63
-
64
- contact_ids.map do |id|
65
- self.find_by_id(id, options)
66
- end
67
- end
68
-
69
- def self.exists_with_id?(contact_id, options = {})
70
- raise "Redis connection not set" unless redis = options[:redis]
71
- raise "No id value passed" unless contact_id
24
+ include Zermelo::Records::RedisSet
25
+ include ActiveModel::Serializers::JSON
26
+ self.include_root_in_json = false
27
+ include Swagger::Blocks
72
28
 
73
- redis.exists("contact:#{contact_id}")
74
- end
29
+ include Flapjack::Data::Extensions::Associations
30
+ include Flapjack::Data::Extensions::ShortName
75
31
 
76
- def self.add(contact_data, options = {})
77
- raise "Redis connection not set" unless redis = options[:redis]
78
- contact_id = contact_data['id']
79
- raise "Contact id value not provided" if contact_id.nil?
32
+ define_attributes :name => :string,
33
+ :timezone => :string
80
34
 
81
- if contact = self.find_by_id(contact_id, :redis => redis)
82
- contact.delete!
83
- end
35
+ index_by :name
84
36
 
85
- self.add_or_update(contact_id, contact_data, :redis => redis)
86
- contact = self.find_by_id(contact_id, :redis => redis)
37
+ has_many :rules, :class_name => 'Flapjack::Data::Rule',
38
+ :inverse_of => :contact
87
39
 
88
- unless contact.nil?
89
- contact.notification_rules # invoke to create general rule
90
- end
40
+ has_many :media, :class_name => 'Flapjack::Data::Medium',
41
+ :inverse_of => :contact
91
42
 
92
- contact
93
- end
43
+ has_and_belongs_to_many :tags, :class_name => 'Flapjack::Data::Tag',
44
+ :inverse_of => :contacts
94
45
 
95
- def self.delete_all(options = {})
96
- raise "Redis connection not set" unless redis = options[:redis]
46
+ validates_with Flapjack::Data::Validators::IdValidator
97
47
 
98
- self.all(:redis => redis).each do |contact|
99
- contact.delete!
100
- end
48
+ validates_each :timezone, :allow_nil => true do |record, att, value|
49
+ record.errors.add(att, 'must be a valid time zone string') if ActiveSupport::TimeZone[value].nil?
101
50
  end
102
51
 
103
- # ensure that instance variables match redis state
104
- # TODO may want to make this protected/private, it's only
105
- # used in this class
106
- def refresh
107
- fn, ln, em = @redis.hmget("contact:#{@id}", 'first_name', 'last_name', 'email')
108
- self.first_name = Flapjack.sanitize(fn)
109
- self.last_name = Flapjack.sanitize(ln)
110
- self.email = Flapjack.sanitize(em)
111
- self.media = @redis.hgetall("contact_media:#{@id}")
112
- self.media_intervals = @redis.hgetall("contact_media_intervals:#{self.id}")
113
- self.media_rollup_thresholds = @redis.hgetall("contact_media_rollup_thresholds:#{self.id}")
114
-
115
- # similar to code in instance method pagerduty_credentials
116
- if service_key = @redis.hget("contact_media:#{@id}", 'pagerduty')
117
- self.pagerduty_credentials =
118
- @redis.hgetall("contact_pagerduty:#{@id}").merge('service_key' => service_key)
119
- end
52
+ before_destroy :remove_child_records
53
+ def remove_child_records
54
+ self.media.each {|medium| medium.destroy }
55
+ self.rules.each {|rule| rule.destroy }
120
56
  end
121
57
 
122
- def update(contact_data)
123
- self.class.add_or_update(@id, contact_data, :redis => @redis)
124
- self.refresh
58
+ def time_zone
59
+ return nil if self.timezone.nil?
60
+ ActiveSupport::TimeZone[self.timezone]
125
61
  end
126
62
 
127
- def delete!
128
- # remove entity & check registrations -- ugh, this will be slow.
129
- # rather than check if the key is present we'll just request its
130
- # deletion anyway, fewer round-trips
131
- @redis.keys('contacts_for:*').each do |cfk|
132
- @redis.srem(cfk, self.id)
133
- end
134
-
135
- @redis.del("drop_alerts_for_contact:#{self.id}")
136
- dafc = @redis.keys("drop_alerts_for_contact:#{self.id}:*")
137
- @redis.del(*dafc) unless dafc.empty?
138
-
139
- # TODO if implemented, alerts_by_contact & alerts_by_check:
140
- # list all alerts from all matched keys, remove them from
141
- # the main alerts sorted set, remove all alerts_by sorted sets
142
- # for the contact
143
-
144
- # remove all associated notification rules
145
- self.notification_rules.each do |nr|
146
- self.delete_notification_rule(nr)
147
- end
63
+ def checks
64
+ time = Time.now
148
65
 
149
- @redis.del("contact:#{self.id}", "contact_media:#{self.id}",
150
- "contact_media_intervals:#{self.id}",
151
- "contact_media_rollup_thresholds:#{self.id}",
152
- "contact_tz:#{self.id}", "contact_pagerduty:#{self.id}")
153
- end
66
+ global_acceptors = self.rules.intersect(:enabled => true,
67
+ :blackhole => false, :strategy => 'global')
154
68
 
155
- def pagerduty_credentials
156
- return unless service_key = @redis.hget("contact_media:#{self.id}", 'pagerduty')
157
- @redis.hgetall("contact_pagerduty:#{self.id}").
158
- merge('service_key' => service_key)
159
- end
69
+ global_rejector_ids = self.rules.intersect(:enabled => true,
70
+ :blackhole => true, :strategy => 'global').select {|rejector|
160
71
 
161
- def set_pagerduty_credentials(details)
162
- @redis.hset("contact_media:#{self.id}", 'pagerduty', details['service_key'])
163
- @redis.hmset("contact_pagerduty:#{self.id}",
164
- *['subdomain', 'token', 'username', 'password'].collect {|f| [f, details[f]]})
165
- end
72
+ rejector.is_occurring_at?(time, timezone)
73
+ }.map(&:id)
166
74
 
167
- def delete_pagerduty_credentials
168
- @redis.hdel("contact_media:#{self.id}", 'pagerduty')
169
- @redis.del("contact_pagerduty:#{self.id}")
170
- end
75
+ # global blackhole
76
+ return Flapjack::Data::Check.empty unless global_rejector_ids.empty?
171
77
 
172
- # returns false if this contact was already in the set for the entity
173
- def add_entity(entity)
174
- key = "contacts_for:#{entity.id}"
175
- @redis.sadd(key, self.id)
176
- end
78
+ tag_rejector_ids = self.rules.intersect(:enabled => true,
79
+ :blackhole => true, :strategy => ['any_tag', 'all_tags', 'no_tag']).select {|rejector|
177
80
 
178
- # returns false if this contact wasn't in the set for the entity
179
- def remove_entity(entity)
180
- key = "contacts_for:#{entity.id}"
181
- @redis.srem(key, self.id)
182
- end
81
+ rejector.is_occurring_at?(time, timezone)
82
+ }.map(&:id)
183
83
 
184
- # NB ideally contacts_for:* keys would scope the entity and check by an
185
- # input source, for namespacing purposes
186
- def entities(options = {})
187
- @redis.keys('contacts_for:*').inject({}) {|ret, k|
188
- if @redis.sismember(k, self.id)
189
- if k =~ /^contacts_for:([a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])(?::(\w+))?$/
190
- entity_id = $1
191
- check = $2
192
-
193
- entity = nil
194
-
195
- if ret.has_key?(entity_id)
196
- entity = ret[entity_id][:entity]
197
- else
198
- entity = Flapjack::Data::Entity.find_by_id(entity_id, :redis => @redis)
199
- ret[entity_id] = {
200
- :entity => entity
201
- }
202
- # using a set to ensure unique check values
203
- ret[entity_id][:checks] = Set.new if options[:checks]
204
- ret[entity_id][:tags] = entity.tags if entity && options[:tags]
205
- end
206
-
207
- if options[:checks]
208
- # if not registered for the check, then was registered for
209
- # the entity, so add all checks
210
- ret[entity_id][:checks] |= (check || (entity ? entity.check_list : []))
211
- end
212
- end
213
- end
214
- ret
215
- }.values
216
- end
217
-
218
- def self.entity_ids_for(contact_ids, options = {})
219
- raise "Redis connection not set" unless redis = options[:redis]
84
+ tag_acceptors = self.rules.intersect(:enabled => true, :blackhole => false,
85
+ :strategy => ['any_tag', 'all_tags', 'no_tag']).select {|acceptor|
220
86
 
221
- entity_ids = {}
87
+ acceptor.is_occurring_at?(time, timezone)
88
+ }
222
89
 
223
- temp_set = SecureRandom.uuid
224
- redis.sadd(temp_set, contact_ids)
90
+ # no positives
91
+ return Flapjack::Data::Check.empty if tag_acceptors.empty?
225
92
 
226
- redis.keys('contacts_for:*').each do |k|
227
- contact_ids = redis.sinter(k, temp_set)
228
- next if contact_ids.empty?
229
- next unless k =~ /^contacts_for:([a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])(?::(\w+))?$/
230
93
 
231
- entity_id = $1
232
- # check = $2
94
+ # initial scope is all enabled
95
+ linked_checks = Flapjack::Data::Check.intersect(:enabled => true)
233
96
 
234
- contact_ids.each do |contact_id|
235
- entity_ids[contact_id] ||= []
236
- entity_ids[contact_id] << entity_id
237
- end
97
+ if global_acceptors.empty?
98
+ # if no global acceptor, scope by matching tags
99
+ tag_acceptor_checks = Flapjack::Data::Rule.matching_checks(tag_acceptors.map(&:id))
100
+ linked_checks = linked_checks.intersect(:id => tag_acceptor_checks)
238
101
  end
239
102
 
240
- redis.del(temp_set)
241
-
242
- entity_ids
243
- end
244
-
245
- def name
246
- [(self.first_name || ''), (self.last_name || '')].join(" ").strip
247
- end
103
+ # then exclude by checks with tags matching rejector, if any
104
+ tag_rejector_checks = Flapjack::Data::Rule.matching_checks(tag_rejector_ids)
105
+ unless tag_rejector_checks.empty?
106
+ linked_checks = linked_checks.diff(:id => tag_rejector_checks)
107
+ end
248
108
 
249
- def notification_rule_ids
250
- @redis.smembers("contact_notification_rules:#{self.id}")
109
+ linked_checks
251
110
  end
252
111
 
253
- # return an array of the notification rules of this contact
254
- def notification_rules(opts = {})
255
- rules = self.notification_rule_ids.inject([]) do |ret, rule_id|
256
- unless (rule_id.nil? || rule_id == '')
257
- ret << Flapjack::Data::NotificationRule.find_by_id(rule_id, :redis => @redis)
258
- end
259
- ret
112
+ swagger_schema :Contact do
113
+ key :required, [:id, :type, :name]
114
+ property :id do
115
+ key :type, :string
116
+ key :format, :uuid
260
117
  end
261
- if rules.all? {|r| r.is_specific? } # also true if empty
262
- rule = self.add_notification_rule({
263
- :entities => [],
264
- :regex_entities => [],
265
- :tags => Set.new([]),
266
- :regex_tags => Set.new([]),
267
- :time_restrictions => [],
268
- :warning_media => ALL_MEDIA,
269
- :critical_media => ALL_MEDIA,
270
- :warning_blackhole => false,
271
- :critical_blackhole => false,
272
- }, :logger => opts[:logger])
273
- rules.unshift(rule)
118
+ property :type do
119
+ key :type, :string
120
+ key :enum, [Flapjack::Data::Contact.short_model_name.singular]
274
121
  end
275
- rules
276
- end
277
-
278
- def add_notification_rule(rule_data, opts = {})
279
- if logger = opts[:logger]
280
- logger.debug("add_notification_rule: contact_id: #{self.id} (#{self.id.class})")
122
+ property :name do
123
+ key :type, :string
281
124
  end
282
- Flapjack::Data::NotificationRule.add(rule_data.merge(:contact_id => self.id),
283
- :redis => @redis, :logger => opts[:logger])
284
- end
285
-
286
- # move an existing notification rule from another contact to this one
287
- def grab_notification_rule(rule)
288
- @redis.srem("contact_notification_rules:#{rule.contact.id}", rule.id)
289
- rule.contact_id = self.id
290
- rule.update({})
291
- @redis.sadd("contact_notification_rules:#{self.id}", rule.id)
292
- end
293
-
294
- def delete_notification_rule(rule)
295
- @redis.srem("contact_notification_rules:#{self.id}", rule.id)
296
- @redis.del("notification_rule:#{rule.id}")
297
- end
298
-
299
- # how often to notify this contact on the given media
300
- # return 15 mins if no value is set
301
- def interval_for_media(media)
302
- interval = @redis.hget("contact_media_intervals:#{self.id}", media)
303
- (interval.nil? || (interval.to_i <= 0)) ? (15 * 60) : interval.to_i
304
- end
305
-
306
- def set_interval_for_media(media, interval)
307
- return if 'pagerduty'.eql?(media)
308
- if interval.nil?
309
- @redis.hdel("contact_media_intervals:#{self.id}", media)
310
- return
125
+ property :timezone do
126
+ key :type, :string
127
+ key :format, :tzinfo
311
128
  end
312
- @redis.hset("contact_media_intervals:#{self.id}", media, interval)
313
- self.media_intervals = @redis.hgetall("contact_media_intervals:#{self.id}")
314
- end
315
-
316
- def rollup_threshold_for_media(media)
317
- threshold = @redis.hget("contact_media_rollup_thresholds:#{self.id}", media)
318
- (threshold.nil? || (threshold.to_i <= 0 )) ? nil : threshold.to_i
319
- end
320
-
321
- def set_rollup_threshold_for_media(media, threshold)
322
- return if 'pagerduty'.eql?(media)
323
- if threshold.nil?
324
- @redis.hdel("contact_media_rollup_thresholds:#{self.id}", media)
325
- return
129
+ property :relationships do
130
+ key :"$ref", :ContactLinks
326
131
  end
327
- @redis.hset("contact_media_rollup_thresholds:#{self.id}", media, threshold)
328
- self.media_rollup_thresholds = @redis.hgetall("contact_media_rollup_thresholds:#{self.id}")
329
132
  end
330
133
 
331
- def set_address_for_media(media, address)
332
- return if 'pagerduty'.eql?(media)
333
- @redis.hset("contact_media:#{self.id}", media, address)
334
- self.media = @redis.hgetall("contact_media:#{@id}")
335
- end
336
-
337
- def remove_media(media)
338
- @redis.hdel("contact_media:#{self.id}", media)
339
- @redis.hdel("contact_media_intervals:#{self.id}", media)
340
- @redis.hdel("contact_media_rollup_thresholds:#{self.id}", media)
341
- if media == 'pagerduty'
342
- @redis.del("contact_pagerduty:#{self.id}")
134
+ swagger_schema :ContactLinks do
135
+ key :required, [:checks, :media, :rules]
136
+ property :checks do
137
+ key :"$ref", :ChecksLinkage
343
138
  end
344
- end
345
-
346
- # drop notifications for
347
- def drop_notifications?(opts = {})
348
- media = opts[:media]
349
- check = opts[:check]
350
- state = opts[:state]
351
-
352
- # build it and they will come
353
- @redis.exists("drop_alerts_for_contact:#{self.id}") ||
354
- (media && @redis.exists("drop_alerts_for_contact:#{self.id}:#{media}")) ||
355
- (media && check &&
356
- @redis.exists("drop_alerts_for_contact:#{self.id}:#{media}:#{check}")) ||
357
- (media && check && state &&
358
- @redis.exists("drop_alerts_for_contact:#{self.id}:#{media}:#{check}:#{state}"))
359
- end
360
-
361
- def update_sent_alert_keys(opts = {})
362
- media = opts[:media]
363
- check = opts[:check]
364
- state = opts[:state]
365
- delete = !! opts[:delete]
366
- key = "drop_alerts_for_contact:#{self.id}:#{media}:#{check}:#{state}"
367
- if delete
368
- @redis.del(key)
369
- else
370
- @redis.set(key, 'd')
371
- @redis.expire(key, self.interval_for_media(media))
372
- # TODO: #182 - update the alert history keys
139
+ property :media do
140
+ key :"$ref", :MediaLinkage
373
141
  end
374
- end
375
-
376
- def drop_rollup_notifications_for_media?(media)
377
- @redis.exists("drop_rollup_alerts_for_contact:#{self.id}:#{media}")
378
- end
379
-
380
- def update_sent_rollup_alert_keys_for_media(media, opts = {})
381
- delete = !! opts[:delete]
382
- key = "drop_rollup_alerts_for_contact:#{self.id}:#{media}"
383
- if delete
384
- @redis.del(key)
385
- else
386
- @redis.set(key, 'd')
387
- @redis.expire(key, self.interval_for_media(media))
142
+ property :rules do
143
+ key :"$ref", :RulesLinkage
388
144
  end
389
- end
390
-
391
- def add_alerting_check_for_media(media, event_id)
392
- @redis.zadd("contact_alerting_checks:#{self.id}:media:#{media}", Time.now.to_i, event_id)
393
- end
394
-
395
- def remove_alerting_check_for_media(media, event_id)
396
- @redis.zrem("contact_alerting_checks:#{self.id}:media:#{media}", event_id)
397
- end
398
-
399
- # removes any checks that are in ok, scheduled or unscheduled maintenance,
400
- # or are disabled from the alerting checks set for the given media;
401
- # returns whether this cleaning moved the medium from rollup to recovery
402
- def clean_alerting_checks_for_media(media)
403
- cleaned = 0
404
-
405
- alerting_checks = alerting_checks_for_media(media)
406
- rollup_threshold = rollup_threshold_for_media(media)
407
-
408
- alerting_checks.each do |check|
409
- entity_check = Flapjack::Data::EntityCheck.for_event_id(check, :redis => @redis)
410
- next unless Flapjack::Data::EntityCheck.state_for_event_id?(check, :redis => @redis) == 'ok' ||
411
- Flapjack::Data::EntityCheck.in_unscheduled_maintenance_for_event_id?(check, :redis => @redis) ||
412
- Flapjack::Data::EntityCheck.in_scheduled_maintenance_for_event_id?(check, :redis => @redis) ||
413
- !entity_check.enabled? ||
414
- !entity_check.contacts.map {|c| c.id}.include?(self.id)
415
-
416
- # FIXME: why can't i get this logging when called from notifier (notification.rb)?
417
- @logger.debug("removing from alerting checks for #{self.id}/#{media}: #{check}") if @logger
418
- remove_alerting_check_for_media(media, check)
419
- cleaned += 1
145
+ property :tags do
146
+ key :"$ref", :TagsLinkage
420
147
  end
421
-
422
- return false if rollup_threshold.nil? || (rollup_threshold <= 0) ||
423
- (alerting_checks.size < rollup_threshold)
424
-
425
- return(cleaned > (alerting_checks.size - rollup_threshold))
426
- end
427
-
428
- def alerting_checks_for_media(media)
429
- @redis.zrange("contact_alerting_checks:#{self.id}:media:#{media}", 0, -1)
430
148
  end
431
149
 
432
- def count_alerting_checks_for_media(media)
433
- @redis.zcard("contact_alerting_checks:#{self.id}:media:#{media}")
434
- end
435
-
436
- # return a list of media enabled for this contact
437
- # eg [ 'email', 'sms' ]
438
- def media_list
439
- @redis.hkeys("contact_media:#{self.id}") - ['pagerduty']
440
- end
441
-
442
- def media_ids
443
- self.media_list.collect {|medium| "#{self.id}_#{medium}" }
150
+ swagger_schema :ContactCreate do
151
+ key :required, [:type, :name]
152
+ property :id do
153
+ key :type, :string
154
+ key :format, :uuid
155
+ end
156
+ property :type do
157
+ key :type, :string
158
+ key :enum, [Flapjack::Data::Contact.short_model_name.singular]
159
+ end
160
+ property :name do
161
+ key :type, :string
162
+ end
163
+ property :timezone do
164
+ key :type, :string
165
+ key :format, :tzinfo
166
+ end
167
+ property :relationships do
168
+ key :"$ref", :ContactCreateLinks
169
+ end
444
170
  end
445
171
 
446
- def timezone
447
- @redis.get("contact_tz:#{self.id}")
172
+ swagger_schema :ContactCreateLinks do
173
+ property :tags do
174
+ key :"$ref", :data_TagsReference
175
+ end
448
176
  end
449
177
 
450
- def timezone=(tz_string)
451
- if tz_string.nil?
452
- @redis.del("contact_tz:#{self.id}")
453
- elsif tz_string.is_a?(String) && !ActiveSupport::TimeZone[tz_string].nil?
454
- @redis.set("contact_tz:#{self.id}", tz_string)
178
+ swagger_schema :ContactUpdate do
179
+ key :required, [:id, :type]
180
+ property :id do
181
+ key :type, :string
182
+ key :format, :uuid
183
+ end
184
+ property :type do
185
+ key :type, :string
186
+ key :enum, [Flapjack::Data::Contact.short_model_name.singular]
187
+ end
188
+ property :name do
189
+ key :type, :string
190
+ end
191
+ property :timezone do
192
+ key :type, :string
193
+ key :format, :tzinfo
194
+ end
195
+ property :relationships do
196
+ key :"$ref", :ContactUpdateLinks
455
197
  end
456
198
  end
457
199
 
458
- def time_zone
459
- return nil if self.timezone.nil?
460
- ActiveSupport::TimeZone[self.timezone]
200
+ swagger_schema :ContactUpdateLinks do
201
+ property :media do
202
+ key :"$ref", :data_MediaReference
203
+ end
204
+ property :rules do
205
+ key :"$ref", :data_RulesReference
206
+ end
207
+ property :tags do
208
+ key :"$ref", :data_TagsReference
209
+ end
461
210
  end
462
211
 
463
- def to_jsonapi(opts = {})
464
- json_data = {
465
- "id" => self.id,
466
- "first_name" => self.first_name,
467
- "last_name" => self.last_name,
468
- "email" => self.email,
469
- "timezone" => self.timezone,
470
- "links" => {
471
- :entities => opts[:entity_ids] || [],
472
- :media => self.media_ids || [],
473
- :notification_rules => self.notification_rule_ids || [],
474
- }
212
+ def self.swagger_included_classes
213
+ # hack -- hardcoding for now
214
+ [
215
+ Flapjack::Data::Check,
216
+ Flapjack::Data::Contact,
217
+ Flapjack::Data::Medium,
218
+ Flapjack::Data::Rule,
219
+ Flapjack::Data::ScheduledMaintenance,
220
+ Flapjack::Data::State,
221
+ Flapjack::Data::Tag,
222
+ Flapjack::Data::UnscheduledMaintenance
223
+ ]
224
+ end
225
+
226
+ def self.jsonapi_methods
227
+ @jsonapi_methods ||= {
228
+ :post => Flapjack::Gateways::JSONAPI::Data::MethodDescriptor.new(
229
+ :attributes => [:name, :timezone],
230
+ :descriptions => {
231
+ :singular => "Create a contact.",
232
+ :multiple => "Create contacts."
233
+ }
234
+ ),
235
+ :get => Flapjack::Gateways::JSONAPI::Data::MethodDescriptor.new(
236
+ :attributes => [:name, :timezone],
237
+ :descriptions => {
238
+ :singular => "Get data for a contact.",
239
+ :multiple => "Get data for multiple contacts."
240
+ }
241
+ ),
242
+ :patch => Flapjack::Gateways::JSONAPI::Data::MethodDescriptor.new(
243
+ :attributes => [:name, :timezone],
244
+ :descriptions => {
245
+ :singular => "Update a contact record.",
246
+ :multiple => "Update contact records."
247
+ }
248
+ ),
249
+ :delete => Flapjack::Gateways::JSONAPI::Data::MethodDescriptor.new(
250
+ :descriptions => {
251
+ :singular => "Delete a contact.",
252
+ :multiple => "Delete contacts."
253
+ }
254
+ ),
475
255
  }
476
- Flapjack.dump_json(json_data)
477
- end
478
-
479
- private
480
-
481
- def initialize(options = {})
482
- raise "Redis connection not set" unless @redis = options[:redis]
483
- @id = options[:id]
484
- @logger = options[:logger]
485
256
  end
486
257
 
487
- # NB: should probably be called in the context of a Redis multi block; not doing so
488
- # here as calling classes may well be adding/updating multiple records in the one
489
- # operation
490
- def self.add_or_update(contact_id, contact_data, options = {})
491
- raise "Redis connection not set" unless redis = options[:redis]
492
-
493
- attrs = (['first_name', 'last_name', 'email'] & contact_data.keys).collect do |key|
494
- [key, contact_data[key]]
495
- end.flatten(1)
496
-
497
- redis.hmset("contact:#{contact_id}", *attrs) unless attrs.empty?
498
-
499
- unless contact_data['media'].nil?
500
- redis.del("contact_media:#{contact_id}")
501
- redis.del("contact_media_intervals:#{contact_id}")
502
- redis.del("contact_media_rollup_thresholds:#{contact_id}")
503
- redis.del("contact_pagerduty:#{contact_id}")
504
-
505
- contact_data['media'].each_pair {|medium, details|
506
- case medium
507
- when 'pagerduty'
508
- redis.hset("contact_media:#{contact_id}", medium, details['service_key'])
509
- redis.hmset("contact_pagerduty:#{contact_id}",
510
- *['subdomain', 'token', 'username', 'password'].collect {|f| [f, details[f]]})
511
- else
512
- redis.hset("contact_media:#{contact_id}", medium, details['address'])
513
- redis.hset("contact_media_intervals:#{contact_id}", medium, details['interval']) if details['interval']
514
- redis.hset("contact_media_rollup_thresholds:#{contact_id}", medium, details['rollup_threshold']) if details['rollup_threshold']
515
- end
258
+ def self.jsonapi_associations
259
+ unless instance_variable_defined?('@jsonapi_associations')
260
+ @jsonapi_associations = {
261
+ :checks => Flapjack::Gateways::JSONAPI::Data::JoinDescriptor.new(
262
+ :get => true,
263
+ :number => :multiple, :link => true, :includable => true,
264
+ :type => 'check',
265
+ :klass => Flapjack::Data::Check,
266
+ :descriptions => {
267
+ :get => "Returns checks which this contact's notification " \
268
+ "rules allow it to receive notifications."
269
+ }
270
+ ),
271
+ :media => Flapjack::Gateways::JSONAPI::Data::JoinDescriptor.new(
272
+ :get => true,
273
+ :number => :multiple, :link => true, :includable => true,
274
+ :descriptions => {
275
+ :get => "Returns media belonging to the contact."
276
+ }
277
+ ),
278
+ :rules => Flapjack::Gateways::JSONAPI::Data::JoinDescriptor.new(
279
+ :get => true,
280
+ :number => :multiple, :link => true, :includable => true,
281
+ :descriptions => {
282
+ :get => "Returns rules belonging to the contact."
283
+ }
284
+ ),
285
+ :tags => Flapjack::Gateways::JSONAPI::Data::JoinDescriptor.new(
286
+ :post => true, :get => true, :patch => true, :delete => true,
287
+ :number => :multiple, :link => true, :includable => true,
288
+ :descriptions => {
289
+ :post => "Associate tags with this contact.",
290
+ :get => "Returns all tags linked to this contact.",
291
+ :patch => "Update the tags associated with this contact.",
292
+ :delete => "Delete associations between tags and this contact."
293
+ }
294
+ )
516
295
  }
296
+ populate_association_data(@jsonapi_associations)
517
297
  end
518
- if contact_data.key?('timezone')
519
- tz = contact_data['timezone']
520
- if tz.nil?
521
- redis.del("contact_tz:#{contact_id}")
522
- elsif tz.is_a?(String) && !ActiveSupport::TimeZone[tz].nil?
523
- redis.set("contact_tz:#{contact_id}", tz )
524
- end
525
- end
298
+ @jsonapi_associations
526
299
  end
527
-
528
300
  end
529
-
530
301
  end
531
-
532
302
  end