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,239 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'sinatra/base'
4
+
5
+ module Flapjack
6
+ module Gateways
7
+ class JSONAPI < Sinatra::Base
8
+ module Methods
9
+ module ResourcePatch
10
+
11
+ def self.registered(app)
12
+ app.helpers Flapjack::Gateways::JSONAPI::Helpers::Headers
13
+ app.helpers Flapjack::Gateways::JSONAPI::Helpers::Miscellaneous
14
+ app.helpers Flapjack::Gateways::JSONAPI::Helpers::Resources
15
+ app.helpers Flapjack::Gateways::JSONAPI::Helpers::Serialiser
16
+
17
+ Flapjack::Gateways::JSONAPI::RESOURCE_CLASSES.each do |resource_class|
18
+ jsonapi_method = resource_class.jsonapi_methods[:patch]
19
+
20
+ unless jsonapi_method.nil?
21
+
22
+ jsonapi_links = resource_class.jsonapi_associations
23
+
24
+ singular_links = jsonapi_links.select {|n, jd|
25
+ jd.send(:patch).is_a?(TrueClass) && :singular.eql?(jd.number)
26
+ }
27
+
28
+ multiple_links = jsonapi_links.select {|n, jd|
29
+ jd.send(:post).is_a?(TrueClass) && :multiple.eql?(jd.number)
30
+ }
31
+
32
+ resource = resource_class.short_model_name.plural
33
+
34
+ app.class_eval do
35
+ single = resource_class.short_model_name.singular
36
+
37
+ model_type = resource_class.short_model_name.name
38
+ model_type_plural = model_type.pluralize
39
+
40
+ model_type_update = "#{model_type}Update".to_sym
41
+
42
+ swagger_path "/#{resource}/{#{single}_id}" do
43
+ operation :patch do
44
+ key :description, jsonapi_method.descriptions[:singular]
45
+ key :operationId, "update_#{single}"
46
+ key :consumes, [JSONAPI_MEDIA_TYPE]
47
+ parameter do
48
+ key :name, "#{single}_id".to_sym
49
+ key :in, :path
50
+ key :description, "Id of a #{single}"
51
+ key :required, true
52
+ key :type, :string
53
+ key :format, :uuid
54
+ end
55
+ parameter do
56
+ key :name, :data
57
+ key :in, :body
58
+ key :description, "#{model_type} to update"
59
+ key :required, true
60
+ schema do
61
+ key :"$ref", model_type_update
62
+ end
63
+ end
64
+ response 204 do
65
+ key :description, "No Content; #{model_type} update success"
66
+ end
67
+ response 403 do
68
+ key :description, "Forbidden; invalid data"
69
+ schema do
70
+ key :'$ref', :Errors
71
+ end
72
+ end
73
+ response 404 do
74
+ key :description, "Not Found"
75
+ schema do
76
+ key :'$ref', :Errors
77
+ end
78
+ end
79
+ response 409 do
80
+ key :description, "Conflict; request id mismatch"
81
+ schema do
82
+ key :'$ref', :Errors
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ swagger_path "/#{resource}" do
89
+ operation :patch do
90
+ key :description, jsonapi_method.descriptions[:multiple]
91
+ key :operationId, "update_#{resource}"
92
+ key :consumes, [JSONAPI_MEDIA_TYPE_BULK]
93
+ parameter do
94
+ key :name, :data
95
+ key :in, :body
96
+ key :description, "#{resource} to update"
97
+ key :required, true
98
+ schema do
99
+ key :type, :array
100
+ items do
101
+ key :"$ref", model_type_update
102
+ end
103
+ end
104
+ end
105
+ response 204 do
106
+ key :description, "No Content; #{resource} update succeeded"
107
+ end
108
+ response 403 do
109
+ key :description, "Forbidden; invalid data"
110
+ schema do
111
+ key :'$ref', :Errors
112
+ end
113
+ end
114
+ response 404 do
115
+ key :description, "Not Found"
116
+ schema do
117
+ key :'$ref', :Errors
118
+ end
119
+ end
120
+ response 409 do
121
+ key :description, "Conflict; request id mismatch"
122
+ schema do
123
+ key :'$ref', :Errors
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ app.patch %r{^/#{resource}(?:/(.+))?$} do
131
+ resource_id = params[:captures].nil? ? nil :
132
+ params[:captures].first
133
+ status 204
134
+
135
+ resources_data, _ = wrapped_params
136
+
137
+ attributes = jsonapi_method.attributes || []
138
+
139
+ validate_data(resources_data, :attributes => attributes,
140
+ :singular_links => singular_links,
141
+ :multiple_links => multiple_links)
142
+
143
+ ids = resources_data.map {|d| d['id']}
144
+
145
+ klasses_to_lock = resources_data.inject([]) do |memo, d|
146
+ next memo unless d.has_key?('relationships')
147
+ d['relationships'].each_pair do |k, v|
148
+ assoc = jsonapi_links[k.to_sym]
149
+ next if assoc.nil?
150
+ memo |= assoc.lock_klasses
151
+ end
152
+ memo
153
+ end
154
+
155
+ jsonapi_type = resource_class.short_model_name.singular
156
+
157
+ resource_class.jsonapi_lock_method(:patch, klasses_to_lock) do
158
+
159
+ resources = if resource_id.nil?
160
+ resources = resource_class.find_by_ids!(*ids)
161
+ else
162
+ halt(err(409, "Id path/data mismatch")) unless ids.nil? || ids.eql?([resource_id])
163
+ [resource_class.find_by_id!(resource_id)]
164
+ end
165
+
166
+ resources_by_id = resources.each_with_object({}) {|r, o| o[r.id] = r }
167
+
168
+ attribute_types = resource_class.attribute_types
169
+
170
+ resource_links = resources_data.each_with_object({}) do |d, memo|
171
+ r = resources_by_id[d['id']]
172
+ rd = normalise_json_data(attribute_types, d['attributes'] || {})
173
+
174
+ type = d['type']
175
+ halt(err(409, "Resource missing data type")) if type.nil?
176
+ halt(err(409, "Resource data type '#{type}' does not match endpoint type '#{jsonapi_type}'")) unless jsonapi_type.eql?(type)
177
+
178
+ rd.each_pair do |att, value|
179
+ next unless attributes.include?(att.to_sym)
180
+ r.send("#{att}=".to_sym, value)
181
+ end
182
+ halt(err(403, "Validation failed, " + r.errors.full_messages.join(', '))) if r.invalid?
183
+
184
+ links = d['relationships']
185
+ next if links.nil?
186
+
187
+ singular_links.each_pair do |assoc, assoc_klass|
188
+ next unless links.has_key?(assoc.to_s)
189
+ memo[r.id] ||= {}
190
+ memo[r.id][assoc.to_s] = assoc_klass.data_klass.find_by_id!(links[assoc.to_s]['data']['id'])
191
+ end
192
+
193
+ multiple_links.each_pair do |assoc, assoc_klass|
194
+ next unless links.has_key?(assoc.to_s)
195
+ current_assoc_ids = r.send(assoc.to_sym).ids.to_a
196
+ memo[r.id] ||= {}
197
+
198
+ link_ids = links[assoc.to_s]['data'].map {|l| l['id']}
199
+
200
+ to_remove = current_assoc_ids - link_ids
201
+ to_add = link_ids - current_assoc_ids
202
+
203
+ memo[r.id][assoc] = [
204
+ to_remove.empty? ? [] : assoc_klass.data_klass.find_by_ids!(*to_remove),
205
+ to_add.empty? ? [] : assoc_klass.data_klass.find_by_ids!(*to_add)
206
+ ]
207
+ end
208
+ end
209
+
210
+ resources_by_id.each do |r_id, r|
211
+ r.save!
212
+ rl = resource_links[r_id]
213
+ next if rl.nil?
214
+ rl.each_pair do |assoc, value|
215
+ case value
216
+ when Array
217
+ to_remove = value.first
218
+ to_add = value.last
219
+
220
+ r.send(assoc.to_sym).remove(*to_remove) unless to_remove.empty?
221
+ r.send(assoc.to_sym).add(*to_add) unless to_add.empty?
222
+ else
223
+ r.send("#{assoc}=".to_sym, value)
224
+ end
225
+ end
226
+ end
227
+
228
+ end
229
+
230
+ true
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'sinatra/base'
4
+
5
+ module Flapjack
6
+ module Gateways
7
+ class JSONAPI < Sinatra::Base
8
+ module Methods
9
+ module ResourcePost
10
+
11
+ # module Helpers
12
+ # end
13
+
14
+ def self.registered(app)
15
+ app.helpers Flapjack::Gateways::JSONAPI::Helpers::Headers
16
+ app.helpers Flapjack::Gateways::JSONAPI::Helpers::Miscellaneous
17
+ app.helpers Flapjack::Gateways::JSONAPI::Helpers::Resources
18
+ app.helpers Flapjack::Gateways::JSONAPI::Helpers::Serialiser
19
+ # app.helpers Flapjack::Gateways::JSONAPI::Methods::ResourcePost::Helpers
20
+
21
+ Flapjack::Gateways::JSONAPI::RESOURCE_CLASSES.each do |resource_class|
22
+ jsonapi_method = resource_class.jsonapi_methods[:post]
23
+
24
+ unless jsonapi_method.nil?
25
+ resource = resource_class.short_model_name.plural
26
+
27
+ jsonapi_links = if resource_class.respond_to?(:jsonapi_associations)
28
+ resource_class.jsonapi_associations || {}
29
+ else
30
+ {}
31
+ end
32
+
33
+ singular_links = jsonapi_links.select {|n, jd|
34
+ jd.send(:post).is_a?(TrueClass) && :singular.eql?(jd.number)
35
+ }
36
+
37
+ multiple_links = jsonapi_links.select {|n, jd|
38
+ jd.send(:post).is_a?(TrueClass) && :multiple.eql?(jd.number)
39
+ }
40
+
41
+ app.class_eval do
42
+ single = resource_class.short_model_name.singular
43
+
44
+ model_type = resource_class.short_model_name.name
45
+ model_type_create = "#{model_type}Create".to_sym
46
+ model_type_data = "data_#{model_type}".to_sym
47
+
48
+ # TODO how to include plural for same route? swagger won't overload...
49
+
50
+ swagger_path "/#{resource}" do
51
+ operation :post do
52
+ key :description, jsonapi_method.descriptions[:singular]
53
+ key :operationId, "create_#{single}"
54
+ key :consumes, [JSONAPI_MEDIA_TYPE]
55
+ key :produces, [Flapjack::Gateways::JSONAPI.media_type_produced]
56
+ parameter do
57
+ key :name, :data
58
+ key :in, :body
59
+ key :description, "#{single} to create"
60
+ key :required, true
61
+ schema do
62
+ key :"$ref", model_type_create
63
+ end
64
+ end
65
+ response 200 do
66
+ key :description, "#{single} creation success"
67
+ schema do
68
+ key :'$ref', model_type_data
69
+ end
70
+ end
71
+ response 403 do
72
+ key :description, "Forbidden; invalid data"
73
+ schema do
74
+ key :'$ref', :Errors
75
+ end
76
+ end
77
+ response 409 do
78
+ key :description, "Conflict; bad request structure"
79
+ schema do
80
+ key :'$ref', :Errors
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ end
87
+
88
+ app.post "/#{resource}" do
89
+ status 201
90
+
91
+ resources_data, unwrap = wrapped_params
92
+
93
+ attributes = jsonapi_method.attributes || []
94
+
95
+ validate_data(resources_data, :attributes => attributes,
96
+ :singular_links => singular_links,
97
+ :multiple_links => multiple_links)
98
+
99
+ resources = nil
100
+
101
+ data_ids = resources_data.each_with_object([]) do |d, memo|
102
+ d_id = d['id']
103
+ memo << d_id.to_s unless d_id.nil?
104
+ end
105
+
106
+ klasses_to_lock = resources_data.inject([]) do |memo, d|
107
+ next memo unless d.has_key?('relationships')
108
+ d['relationships'].each_pair do |k, v|
109
+ assoc = jsonapi_links[k.to_sym]
110
+ next if assoc.nil?
111
+ memo |= assoc.lock_klasses
112
+ end
113
+ memo
114
+ end
115
+
116
+ attribute_types = resource_class.attribute_types
117
+
118
+ jsonapi_type = resource_class.short_model_name.singular
119
+
120
+ resource_class.jsonapi_lock_method(:post, klasses_to_lock) do
121
+
122
+ unless data_ids.empty? || resource_class.included_modules.include?(Zermelo::Records::Stub)
123
+ conflicted_ids = resource_class.intersect(:id => data_ids).ids
124
+ halt(err(409, "#{resource_class.name.split('::').last.pluralize} already exist with the following ids: " +
125
+ conflicted_ids.to_a.join(', '))) unless conflicted_ids.empty?
126
+ end
127
+ links_by_resource = resources_data.each_with_object({}) do |rd, memo|
128
+ record_data = rd['attributes'].nil? ? {} :
129
+ normalise_json_data(attribute_types, rd['attributes'])
130
+ type = rd['type']
131
+ halt(err(409, "Resource missing data type")) if type.nil?
132
+ halt(err(409, "Resource data type '#{type}' does not match endpoint '#{jsonapi_type}'")) unless jsonapi_type.eql?(type)
133
+ r_id = rd['id']
134
+ record_data[:id] = r_id unless r_id.nil?
135
+ r = resource_class.new(record_data)
136
+
137
+ # hacking in support for non-persisted objects
138
+ case resource_class.name
139
+ when Flapjack::Data::Acknowledgement.name, Flapjack::Data::TestNotification.name
140
+ r.queue = config['processor_queue'] || 'events'
141
+ end
142
+
143
+ halt(err(403, "Validation failed, " + r.errors.full_messages.join(', '))) if r.invalid?
144
+ memo[r] = rd['relationships']
145
+ end
146
+
147
+ # get linked objects, fail before save if we don't find them
148
+ resource_links = links_by_resource.each_with_object({}) do |(r, links), memo|
149
+ next if links.nil?
150
+
151
+ singular_links.each_pair do |assoc, assoc_klass|
152
+ next unless links.has_key?(assoc.to_s)
153
+ memo[r.object_id] ||= {}
154
+ memo[r.object_id][assoc.to_s] = assoc_klass.data_klass.find_by_id!(links[assoc.to_s]['data']['id'])
155
+ end
156
+
157
+ multiple_links.each_pair do |assoc, assoc_klass|
158
+ next unless links.has_key?(assoc.to_s)
159
+ memo[r.object_id] ||= {}
160
+ memo[r.object_id][assoc.to_s] = assoc_klass.data_klass.find_by_ids!(*(links[assoc.to_s]['data'].map {|l| l['id']}))
161
+ end
162
+ end
163
+
164
+ links_by_resource.keys.each do |r|
165
+ r.save!
166
+ rl = resource_links[r.object_id]
167
+ next if rl.nil?
168
+ rl.each_pair do |assoc, value|
169
+ case value
170
+ when Array
171
+ r.send(assoc.to_sym).add(*value) unless value.empty?
172
+ else
173
+ r.send("#{assoc}=".to_sym, value)
174
+ end
175
+ end
176
+ end
177
+ resources = links_by_resource.keys
178
+ end
179
+
180
+ resource_ids = resources.map(&:id)
181
+
182
+ response.headers['Location'] = "#{request.base_url}/#{resource}/#{resource_ids.join(',')}"
183
+
184
+ ajs = as_jsonapi(resource_class, resources, resource_ids,
185
+ :unwrap => unwrap, :query_type => :resource)
186
+
187
+ Flapjack.dump_json(ajs)
188
+ end
189
+ end
190
+
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rack'
4
+
5
+ # Hat-tip to https://github.com/glasnt for the suggestion
6
+
7
+ module Flapjack
8
+ module Gateways
9
+ class JSONAPI < Sinatra::Base
10
+ module Middleware
11
+ class ArrayParamFixer < Struct.new(:app)
12
+ def call(env)
13
+ if (env["REQUEST_METHOD"] == 'GET') && env["rack.request.query_string"].nil?
14
+ qs = env["QUERY_STRING"]
15
+ fixed_qs = qs.to_s.sub(/^filter=/, "filter[]=")
16
+ .gsub(/&filter=/, "&filter[]=")
17
+
18
+ env["rack.request.query_string"] = qs # avoid Rack re-parsing it
19
+ env["rack.request.query_hash"] = ::Rack::Utils.parse_nested_query(fixed_qs)
20
+ end
21
+ app.call(env)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end