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,313 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'sinatra/base'
4
+
5
+ module Flapjack
6
+ module Gateways
7
+ class JSONAPI < Sinatra::Base
8
+ module Helpers
9
+ module Serialiser
10
+
11
+ def as_jsonapi(klass, resources, resources_ids, options = {})
12
+ included = options[:include]
13
+ fields = options[:fields]
14
+ query_type = options[:query_type]
15
+
16
+ payload = {}
17
+
18
+ unless included.nil? || included.empty?
19
+ payload = collect_included_data(klass, resources,
20
+ :include => included, :query_type => query_type)
21
+ end
22
+
23
+ ret = {
24
+ :data => as_jsonapi_data(klass, resources, resources_ids,
25
+ :primary => true, :payload => payload, :fields => fields,
26
+ :unwrap => options[:unwrap])
27
+ }
28
+ unless payload.nil? || payload.empty?
29
+ ret[:included] = included_data_from_payload(payload, :fields => fields)
30
+ end
31
+ ret
32
+ end
33
+
34
+ def as_jsonapi_included(klass, resources, options = {})
35
+ included = options[:include]
36
+ fields = options[:fields]
37
+
38
+ payload = collect_included_data(klass, resources,
39
+ :include => included, :query_type => :association)
40
+
41
+ included_data_from_payload(payload, :fields => fields)
42
+ end
43
+
44
+ def as_jsonapi_data(klass, resources, resources_ids, options = {})
45
+ unwrap = options[:unwrap]
46
+ payload = options[:payload]
47
+
48
+ relationship_scopes = options[:relationship_scopes]
49
+
50
+ fields = extract_fields(options[:fields], klass)
51
+
52
+ whitelist = if klass.respond_to?(:jsonapi_methods)
53
+ method_def = klass.jsonapi_methods[:get] || klass.jsonapi_methods[:post]
54
+ method_def.nil? ? [] : method_def.attributes
55
+ else
56
+ []
57
+ end
58
+
59
+ jsonapi_fields = if fields.nil?
60
+ whitelist
61
+ else
62
+ Set.new(fields).keep_if {|f| whitelist.include?(f) }.to_a
63
+ end
64
+
65
+ links = jsonapi_linkages(klass, resources_ids, :primary => options[:primary],
66
+ :payload => payload, :relationship_scopes => relationship_scopes)
67
+
68
+ resources_as_json = resources.collect do |r|
69
+ r_id = r.id
70
+
71
+ l = links[r_id]
72
+
73
+ data = {
74
+ :type => klass.short_model_name.singular,
75
+ :id => r_id
76
+ }
77
+ attrs = r.as_json(:only => jsonapi_fields)
78
+ data[:attributes] = attrs unless attrs.empty?
79
+ data[:relationships] = l unless l.nil? || l.empty?
80
+ data
81
+ end
82
+
83
+ unless (resources_as_json.size == 1) && unwrap.is_a?(TrueClass)
84
+ return resources_as_json
85
+ end
86
+
87
+ resources_as_json.first
88
+ end
89
+
90
+ def lock_for_include_clause(klass, options = {})
91
+ included = options[:include]
92
+
93
+ locks = Set.new
94
+
95
+ included_parts = included.map {|i| i.split('.')}.sort_by(&:length)
96
+
97
+ included_parts.each do |incl_fragments|
98
+ incl_todo = incl_fragments
99
+ incl_done = []
100
+
101
+ last_klass = klass
102
+
103
+ until incl_todo.empty?
104
+ fragment = incl_todo.shift
105
+ last_klass, lock_k = resolve_include_locks(last_klass, fragment)
106
+ incl_done.push(fragment)
107
+ locks |= lock_k
108
+ end
109
+ end
110
+
111
+ locks.to_a
112
+ end
113
+
114
+ def resolve_include_locks(parent_klass, name)
115
+ links = if parent_klass.respond_to?(:jsonapi_associations)
116
+ parent_klass.jsonapi_associations || {}
117
+ else
118
+ {}
119
+ end
120
+
121
+ assoc_data = links[name.to_sym]
122
+
123
+ return if assoc_data.nil? || !assoc_data.link
124
+
125
+ klass = assoc_data.data_klass
126
+
127
+ [klass, assoc_data.lock_klasses]
128
+ end
129
+
130
+ def collect_included_data(klass, resources, options = {})
131
+ included = options[:include]
132
+ query_type = options[:query_type]
133
+
134
+ payload = {}
135
+
136
+ init_node = [klass, nil => resources.ids]
137
+
138
+ included_parts = included.map {|i| i.split('.')}.sort_by(&:length)
139
+
140
+ included_parts.each do |incl_fragments|
141
+ incl_todo = incl_fragments
142
+ incl_done = []
143
+
144
+ last_node = init_node
145
+
146
+ until incl_todo.empty?
147
+ fragment = incl_todo.shift
148
+
149
+ last_node = resolve_include_fragment(last_node, fragment,
150
+ incl_todo, incl_done, :query_type => query_type)
151
+ incl_done.push(fragment)
152
+
153
+ # store new klass, resources as appropriate by joined incl_done
154
+ k = incl_done.join(".")
155
+
156
+ payload[k] ||= [last_node.first]
157
+ payload[k] << last_node.last
158
+ end
159
+ end
160
+
161
+ payload
162
+ end
163
+
164
+ def resolve_include_fragment(node, name, incl_todo, incl_done,
165
+ options = {})
166
+
167
+ query_type = options[:query_type]
168
+
169
+ parent_klass = node.first
170
+ nlv = node.last.values
171
+ parent_resources = if nlv.all? {|v| v.is_a?(Set) || v.is_a?(Array)}
172
+ nlv.reduce(Set.new, :|)
173
+ else
174
+ nlv
175
+ end
176
+
177
+ parent_links = if parent_klass.respond_to?(:jsonapi_associations)
178
+ parent_klass.jsonapi_associations || {}
179
+ else
180
+ {}
181
+ end
182
+
183
+ assoc_data = parent_links[name.to_sym]
184
+
185
+ return [] if assoc_data.nil? ||
186
+ (:resource.eql?(query_type) && !assoc_data.includable) ||
187
+ (:association.eql?(query_type) && !assoc_data.link)
188
+
189
+ fragment_klass = assoc_data.data_klass
190
+
191
+ # NB: if anything's limiting the returned data, it would need
192
+ # to be included here
193
+ scope = parent_klass.intersect(:id => parent_resources)
194
+
195
+ data = if assoc_data.association_data.nil?
196
+ scope.all.each_with_object({}) do |r, memo|
197
+ d = r.send(name.to_sym)
198
+ memo[r.id] = case d
199
+ when nil
200
+ nil
201
+ when Zermelo::Filter, Zermelo::Associations::Multiple
202
+ d.ids
203
+ when Array, Set
204
+ d.map(&:id)
205
+ else
206
+ d.id
207
+ end
208
+ end
209
+ else
210
+ scope.associated_ids_for(name.to_sym)
211
+ end
212
+
213
+ [fragment_klass, data]
214
+ end
215
+
216
+ def included_data_from_payload(payload, options = {})
217
+ fields = options[:fields]
218
+
219
+ scopes_by_klass = {}
220
+ names_by_klass = {}
221
+
222
+ payload.each do |name, pl|
223
+ local_klass = pl.first
224
+
225
+ scopes_by_klass[local_klass] ||= Set.new
226
+ names_by_klass[local_klass] ||= []
227
+ names_by_klass[local_klass] << name
228
+
229
+ data = pl[1..-1].map {|d| d.values }.flatten(1)
230
+
231
+ if data.all? {|d| d.is_a?(Set) || d.is_a?(Array)}
232
+ data.each do |sd|
233
+ scopes_by_klass[local_klass] |= sd
234
+ end
235
+ else
236
+ scopes_by_klass[local_klass] |= data
237
+ end
238
+ end
239
+
240
+ result = scopes_by_klass.inject([]) do |memo, (local_klass, scopes)|
241
+
242
+ memo += as_jsonapi_data(local_klass,
243
+ local_klass.intersect(:id => scopes), scopes,
244
+ :relationship_scopes => names_by_klass[local_klass],
245
+ :payload => payload, :fields => fields)
246
+ memo
247
+ end
248
+
249
+ result
250
+ end
251
+
252
+ def extract_fields(fields, klass)
253
+ return if fields.nil?
254
+ raise "Field limits must be passed as a Hash" unless fields.is_a?(Hash)
255
+ resource_name = klass.short_model_name.singular
256
+ return unless fields.has_key?(resource_name)
257
+ fields[resource_name].split(',').map(&:to_sym)
258
+ end
259
+
260
+ def jsonapi_linkages(klass, resource_ids, opts = {})
261
+ payload = opts[:payload]
262
+
263
+ relationship_scopes = opts[:relationship_scopes]
264
+
265
+ jsonapi_assocs = klass.jsonapi_associations.reject do |jl_name, jl_data|
266
+ jl_data.link.is_a?(FalseClass) && jl_data.includable.is_a?(FalseClass)
267
+ end
268
+
269
+ resources_name = klass.short_model_name.plural
270
+
271
+ resource_ids.each_with_object({}) do |r_id, memo|
272
+
273
+ memo[r_id] = jsonapi_assocs.keys.each_with_object({}) do |jl_name, a_memo|
274
+ linked = {}
275
+
276
+ if opts[:primary]
277
+ linked[:links] = {
278
+ :self => "#{request.base_url}/#{resources_name}/#{r_id}/relationships/#{jl_name}",
279
+ :related => "#{request.base_url}/#{resources_name}/#{r_id}/#{jl_name}"
280
+ }
281
+ end
282
+
283
+ payload_scopes = if relationship_scopes.nil? || relationship_scopes.empty?
284
+ opts[:primary] ? [jl_name.to_s] : []
285
+ else
286
+ relationship_scopes.map {|rls| "#{rls}.#{jl_name}" }
287
+ end
288
+
289
+ payload_scopes.each do |pls|
290
+
291
+ if payload.has_key?(pls) && payload[pls].last.has_key?(r_id)
292
+
293
+ klass = payload[pls].first
294
+ d = payload[pls].last[r_id]
295
+ linked[:data] = case d
296
+ when Set, Array
297
+ d.map {|da| {:type => klass.short_model_name.singular, :id => da} }
298
+ when String
299
+ {:type => klass.short_model_name.singular, :id => d}
300
+ end
301
+ end
302
+ end
303
+
304
+ next if linked.empty?
305
+ a_memo[jl_name] = linked
306
+ end
307
+ end
308
+ end
309
+ end
310
+ end
311
+ end
312
+ end
313
+ end
@@ -0,0 +1,322 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'active_support/concern'
4
+
5
+ module Flapjack
6
+ module Gateways
7
+ class JSONAPI < Sinatra::Base
8
+ module Helpers
9
+ module SwaggerDocs
10
+
11
+ extend ActiveSupport::Concern
12
+
13
+ included do
14
+ swagger_root do
15
+ key :swagger, '2.0'
16
+ info do
17
+ key :version, '2.0.0'
18
+ key :title, 'Flapjack API'
19
+ key :description, ''
20
+ contact do
21
+ key :name, ''
22
+ end
23
+ license do
24
+ key :name, 'MIT'
25
+ end
26
+ end
27
+ key :host, 'localhost'
28
+ key :basePath, '/doc'
29
+ key :schemes, ['http']
30
+ key :consumes, [JSONAPI_MEDIA_TYPE]
31
+ key :produces, [JSONAPI_MEDIA_TYPE]
32
+ end
33
+
34
+ swagger_schema :Linkage do
35
+ key :required, [:related, :self]
36
+ property :related do
37
+ key :type, :string
38
+ key :format, :url
39
+ end
40
+ property :self do
41
+ key :type, :string
42
+ key :format, :url
43
+ end
44
+ end
45
+
46
+ swagger_schema :Links do
47
+ key :required, [:self]
48
+ property :self do
49
+ key :type, :string
50
+ key :format, :url
51
+ end
52
+ property :first do
53
+ key :type, :string
54
+ key :format, :url
55
+ end
56
+ property :last do
57
+ key :type, :string
58
+ key :format, :url
59
+ end
60
+ property :next do
61
+ key :type, :string
62
+ key :format, :url
63
+ end
64
+ property :prev do
65
+ key :type, :string
66
+ key :format, :url
67
+ end
68
+ end
69
+
70
+ swagger_schema :Pagination do
71
+ key :required, [:page, :per_page, :total_pages, :total_count]
72
+ property :page do
73
+ key :type, :integer
74
+ key :format, :int64
75
+ end
76
+ property :per_page do
77
+ key :type, :integer
78
+ key :format, :int64
79
+ end
80
+ property :total_pages do
81
+ key :type, :integer
82
+ key :format, :int64
83
+ end
84
+ property :total_count do
85
+ key :type, :integer
86
+ key :format, :int64
87
+ end
88
+ end
89
+
90
+ swagger_schema :Meta do
91
+ property :pagination do
92
+ key :"$ref", :Pagination
93
+ end
94
+ end
95
+
96
+ swagger_schema :Error do
97
+ property :id do
98
+ key :type, :string
99
+ key :format, :uuid
100
+ end
101
+ property :status do
102
+ key :type, :string
103
+ end
104
+ property :detail do
105
+ key :type, :string
106
+ end
107
+ end
108
+
109
+ swagger_schema :Errors do
110
+ property :errors do
111
+ key :type, :array
112
+ items do
113
+ key :"$ref", :Error
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ class_methods do
120
+ def swagger_wrappers(resource, klass)
121
+ jsonapi_methods = if klass.respond_to?(:jsonapi_methods)
122
+ klass.jsonapi_methods || {}
123
+ else
124
+ {}
125
+ end
126
+
127
+ singular = Flapjack::Gateways::JSONAPI::RESOURCE_CLASSES.map(&:jsonapi_associations).inject([]) do |memo, assoc|
128
+ memo += assoc.values.select {|ja| klass.eql?(ja.data_klass) && :singular.eql?(ja.number) }
129
+ memo
130
+ end
131
+
132
+ multiple = Flapjack::Gateways::JSONAPI::RESOURCE_CLASSES.map(&:jsonapi_associations).inject([]) do |memo, assoc|
133
+ memo += assoc.values.select { |ja| klass.eql?(ja.data_klass) && :multiple.eql?(ja.number) }
134
+ memo
135
+ end
136
+
137
+ included_classes = klass.swagger_included_classes
138
+
139
+ model_type = klass.name.demodulize
140
+ model_type_plural = model_type.pluralize
141
+
142
+ model_type_included = "#{model_type}Included".to_sym
143
+
144
+ model_type_create = jsonapi_methods.key?(:post) ? "#{model_type}Create".to_sym : nil
145
+ model_type_update = jsonapi_methods.key?(:patch) ? "#{model_type}Update".to_sym : nil
146
+
147
+ model_type_data = "data_#{model_type}".to_sym
148
+ model_type_data_plural = "data_#{model_type_plural}".to_sym
149
+
150
+ # model_type_create_data = jsonapi_methods.key?(:post) ? "data_#{model_type_create}".to_sym : nil
151
+ # model_type_create_data_plural = jsonapi_methods.key?(:post) ? "data_#{model_type_plural}Create".to_sym : nil
152
+
153
+ model_type_reference = "#{model_type}Reference".to_sym
154
+ model_type_reference_data = "data_#{model_type_reference}".to_sym
155
+ model_type_reference_data_plural = "data_#{model_type_plural}Reference".to_sym
156
+
157
+ model_type_linkage = "#{model_type}Linkage".to_sym
158
+ model_type_linkage_plural = "#{model_type_plural}Linkage".to_sym
159
+
160
+ if included_classes.empty?
161
+
162
+ swagger_schema model_type_data do
163
+ key :required, [:data]
164
+ property :data do
165
+ key :"$ref", model_type
166
+ end
167
+ property :links do
168
+ key :"$ref", :Links
169
+ end
170
+ end
171
+
172
+ else
173
+ swagger_schema model_type_included do
174
+ key :required, [:type, :id]
175
+ property :type do
176
+ key :type, :string
177
+ key :enum, included_classes.map(&:short_model_name).
178
+ map(&:singular).map(&:to_s)
179
+ end
180
+ property :id do
181
+ key :type, :string
182
+ key :format, :uuid
183
+ end
184
+ end
185
+
186
+ swagger_schema model_type_data do
187
+ key :required, [:data]
188
+ property :data do
189
+ key :"$ref", model_type
190
+ end
191
+ property :included do
192
+ key :type, :array
193
+ items do
194
+ key :"$ref", model_type_included
195
+ end
196
+ end
197
+ property :links do
198
+ key :"$ref", :Links
199
+ end
200
+ end
201
+ end
202
+
203
+ if [:get, :patch, :delete].any? {|m| jsonapi_methods.key?(m) } ||
204
+ !singular.empty? || !multiple.empty?
205
+
206
+ if included_classes.empty?
207
+ swagger_schema model_type_data_plural do
208
+ key :required, [:data]
209
+ property :data do
210
+ key :type, :array
211
+ items do
212
+ key :"$ref", model_type
213
+ end
214
+ end
215
+ property :links do
216
+ key :"$ref", :Links
217
+ end
218
+ property :meta do
219
+ key :"$ref", :Meta
220
+ end
221
+ end
222
+ else
223
+ swagger_schema model_type_data_plural do
224
+ key :required, [:data]
225
+ property :data do
226
+ key :type, :array
227
+ items do
228
+ key :"$ref", model_type
229
+ end
230
+ end
231
+ property :included do
232
+ key :type, :array
233
+ items do
234
+ key :"$ref", model_type_included
235
+ end
236
+ end
237
+ property :links do
238
+ key :"$ref", :Links
239
+ end
240
+ property :meta do
241
+ key :"$ref", :Meta
242
+ end
243
+ end
244
+ end
245
+ end
246
+
247
+ deletable = jsonapi_methods.key?(:delete)
248
+
249
+ unless singular.empty? && multiple.empty? && !deletable
250
+ singular_ref = singular.any? {|s| [:post, :patch].any? {|r| s.send(r) }}
251
+ singular_lnk = singular.any? {|s| [:post, :get, :patch].any? {|r| s.send(r) }}
252
+
253
+ multiple_ref = multiple.any? {|m| [:post, :patch].any? {|r| m.send(r) }}
254
+ multiple_lnk = multiple.any? {|m| [:post, :get, :patch].any? {|r| m.send(r) }}
255
+
256
+ if deletable || singular_ref || singular_lnk || multiple_ref || multiple_lnk
257
+ swagger_schema model_type_reference do
258
+ key :required, [:id, :type]
259
+ property :id do
260
+ key :type, :string
261
+ key :format, :uuid
262
+ end
263
+ property :type do
264
+ key :type, :string
265
+ key :enum, [klass.short_model_name.singular]
266
+ end
267
+ end
268
+ end
269
+
270
+ if singular_ref
271
+ swagger_schema model_type_reference_data do
272
+ key :required, [:data]
273
+ property :data do
274
+ key :"$ref", model_type_reference
275
+ end
276
+ end
277
+ end
278
+
279
+ if singular_lnk
280
+ swagger_schema model_type_linkage do
281
+ property :links do
282
+ key :"$ref", :Linkage
283
+ end
284
+ property :data do
285
+ key :"$ref", model_type_reference
286
+ end
287
+ end
288
+ end
289
+
290
+ if multiple_ref || deletable
291
+ swagger_schema model_type_reference_data_plural do
292
+ key :required, [:data]
293
+ property :data do
294
+ key :type, :array
295
+ items do
296
+ key :"$ref", model_type_reference
297
+ end
298
+ end
299
+ end
300
+ end
301
+
302
+ if multiple_lnk
303
+ swagger_schema model_type_linkage_plural do
304
+ property :links do
305
+ key :"$ref", :Linkage
306
+ end
307
+ property :data do
308
+ key :type, :array
309
+ items do
310
+ key :"$ref", model_type_reference
311
+ end
312
+ end
313
+ end
314
+ end
315
+ end
316
+ end
317
+ end
318
+ end
319
+ end
320
+ end
321
+ end
322
+ end