phobos_checkpoint_ui 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +2 -0
  4. data/CHANGELOG.md +9 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +81 -0
  8. data/Rakefile +12 -0
  9. data/assets/index-52cbf3063583f3c09a4b-0.css +2 -0
  10. data/assets/index-52cbf3063583f3c09a4b-0.css.map +1 -0
  11. data/assets/index-52cbf3063583f3c09a4b-1.css +2 -0
  12. data/assets/index-52cbf3063583f3c09a4b-1.css.map +1 -0
  13. data/assets/index-52cbf3063583f3c09a4b.js +49 -0
  14. data/assets/index-52cbf3063583f3c09a4b.js.map +1 -0
  15. data/assets/index.html +12 -0
  16. data/bin/console +14 -0
  17. data/bin/setup +8 -0
  18. data/circle.yml +29 -0
  19. data/frontend/.babelrc +3 -0
  20. data/frontend/.editorconfig +8 -0
  21. data/frontend/.eslintignore +2 -0
  22. data/frontend/.eslintrc +3 -0
  23. data/frontend/.npmignore +8 -0
  24. data/frontend/package.json +45 -0
  25. data/frontend/sagui.config.js +52 -0
  26. data/frontend/src/actions/event-details.js +42 -0
  27. data/frontend/src/actions/event-details.spec.js +71 -0
  28. data/frontend/src/actions/event-overview.js +11 -0
  29. data/frontend/src/actions/event-overview.spec.js +20 -0
  30. data/frontend/src/actions/event-retry.js +58 -0
  31. data/frontend/src/actions/event-retry.spec.js +117 -0
  32. data/frontend/src/actions/events-search.js +75 -0
  33. data/frontend/src/actions/events-search.spec.js +197 -0
  34. data/frontend/src/actions/flash-messages.js +16 -0
  35. data/frontend/src/actions/flash-messages.spec.js +23 -0
  36. data/frontend/src/actions/index.js +24 -0
  37. data/frontend/src/actions/search-input-filter.js +14 -0
  38. data/frontend/src/actions/search-input-filter.spec.js +20 -0
  39. data/frontend/src/api.js +46 -0
  40. data/frontend/src/components/event/error-message.js +19 -0
  41. data/frontend/src/components/event/error-message.scss +8 -0
  42. data/frontend/src/components/event/event.scss +3 -0
  43. data/frontend/src/components/event/index.js +77 -0
  44. data/frontend/src/components/event/index.spec.js +89 -0
  45. data/frontend/src/components/event/loading.js +16 -0
  46. data/frontend/src/components/event/style.js +16 -0
  47. data/frontend/src/components/event-overview/attribute.js +24 -0
  48. data/frontend/src/components/event-overview/event-overview.scss +28 -0
  49. data/frontend/src/components/event-overview/index.js +47 -0
  50. data/frontend/src/components/event-overview/index.spec.js +67 -0
  51. data/frontend/src/components/event-overview-dialog/event-overview-dialog.scss +15 -0
  52. data/frontend/src/components/event-overview-dialog/index.js +85 -0
  53. data/frontend/src/components/event-retry-dialog/index.js +72 -0
  54. data/frontend/src/components/events-list/events-list.scss +49 -0
  55. data/frontend/src/components/events-list/index.js +62 -0
  56. data/frontend/src/components/events-list/index.spec.js +59 -0
  57. data/frontend/src/components/flash-message/flash-message.scss +40 -0
  58. data/frontend/src/components/flash-message/index.js +45 -0
  59. data/frontend/src/components/flash-message/index.spec.js +59 -0
  60. data/frontend/src/components/flash-message-list/index.js +34 -0
  61. data/frontend/src/components/header/header.scss +29 -0
  62. data/frontend/src/components/header/index.js +44 -0
  63. data/frontend/src/components/search-input/index.js +104 -0
  64. data/frontend/src/components/search-input/index.spec.js +56 -0
  65. data/frontend/src/components/search-input/search-input.scss +7 -0
  66. data/frontend/src/configs.js +15 -0
  67. data/frontend/src/helpers.spec.js +2 -0
  68. data/frontend/src/index.html +12 -0
  69. data/frontend/src/index.js +26 -0
  70. data/frontend/src/index.scss +31 -0
  71. data/frontend/src/reducers/event-details.js +32 -0
  72. data/frontend/src/reducers/event-details.spec.js +54 -0
  73. data/frontend/src/reducers/events-filters.js +17 -0
  74. data/frontend/src/reducers/events-filters.spec.js +34 -0
  75. data/frontend/src/reducers/events.js +48 -0
  76. data/frontend/src/reducers/events.spec.js +95 -0
  77. data/frontend/src/reducers/flash-messages.js +14 -0
  78. data/frontend/src/reducers/flash-messages.spec.js +34 -0
  79. data/frontend/src/reducers/index.js +18 -0
  80. data/frontend/src/reducers/index.spec.js +20 -0
  81. data/frontend/src/reducers/xhr-status.js +75 -0
  82. data/frontend/src/reducers/xhr-status.spec.js +94 -0
  83. data/frontend/src/routes.js +20 -0
  84. data/frontend/src/store.js +15 -0
  85. data/frontend/src/views/event-details.js +50 -0
  86. data/frontend/src/views/events-search.js +112 -0
  87. data/frontend/src/views/events-search.scss +24 -0
  88. data/frontend/src/views/events-search.spec.js +96 -0
  89. data/frontend/src/views/layout.js +24 -0
  90. data/frontend/src/views/layout.scss +3 -0
  91. data/lib/phobos_checkpoint_ui/app.rb +11 -0
  92. data/lib/phobos_checkpoint_ui/static_app.rb +19 -0
  93. data/lib/phobos_checkpoint_ui/tasks.rb +20 -0
  94. data/lib/phobos_checkpoint_ui/version.rb +3 -0
  95. data/lib/phobos_checkpoint_ui.rb +10 -0
  96. data/phobos_checkpoint_ui.gemspec +53 -0
  97. data/screenshot1.png +0 -0
  98. data/screenshot2.png +0 -0
  99. metadata +267 -0
data/assets/index.html ADDED
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>PhobosCheckpoint</title>
5
+ <meta charset="utf-8" />
6
+ <link href="/assets/index-52cbf3063583f3c09a4b-1.css" rel="stylesheet"><link href="/assets/index-52cbf3063583f3c09a4b-0.css" rel="stylesheet"></head>
7
+ <body>
8
+ <div id="root">
9
+ <div class='loading-boot'>Loading...</div>
10
+ </div>
11
+ <script type="text/javascript" src="/assets/index-52cbf3063583f3c09a4b.js"></script></body>
12
+ </html>
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "phobos_checkpoint_ui"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/circle.yml ADDED
@@ -0,0 +1,29 @@
1
+ machine:
2
+ pre:
3
+ - curl -sSL https://s3.amazonaws.com/circle-downloads/install-circleci-docker.sh | bash -s -- 1.10.0
4
+ services:
5
+ - docker
6
+ environment:
7
+ LOG_LEVEL: DEBUG
8
+ CI: true
9
+ ruby:
10
+ version: 2.3.1
11
+ node:
12
+ version: 6.3.0
13
+
14
+ # Ignores circle ci default database setup
15
+ database:
16
+ override:
17
+ - echo "overrides circle CI commands"
18
+
19
+ dependencies:
20
+ pre:
21
+ - docker -v
22
+ - gem install bundler -v 1.9.5
23
+ - bundle install
24
+ - cd frontend; npm install
25
+
26
+ test:
27
+ override:
28
+ - bundle exec rspec -r rspec_junit_formatter --format RspecJunitFormatter -o $CIRCLE_TEST_REPORTS/rspec/unit.xml
29
+ - cd frontend; npm run test:unit
data/frontend/.babelrc ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "presets": ["sagui"]
3
+ }
@@ -0,0 +1,8 @@
1
+ root = true
2
+
3
+ [*]
4
+ end_of_line = lf
5
+ insert_final_newline = true
6
+ trim_trailing_whitespace = true
7
+ indent_style = space
8
+ indent_size = 2
@@ -0,0 +1,2 @@
1
+ coverage
2
+ dist
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "sagui"
3
+ }
@@ -0,0 +1,8 @@
1
+ .babelrc
2
+ .editorconfig
3
+ .eslintignore
4
+ .eslintrc
5
+ coverage
6
+ node_modules
7
+ npm-debug.log
8
+ src
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "phobos_checkpoint_ui",
3
+ "version": "1.0.0",
4
+ "description": "Phobos Checkpoint UI is a GUI for phobos checkpoint API",
5
+ "main": "index.js",
6
+ "private": true,
7
+ "engines": {
8
+ "node": ">= 6.3.x"
9
+ },
10
+ "scripts": {
11
+ "test": "npm run test:lint && npm run test:unit",
12
+ "build": "sagui build",
13
+ "develop": "sagui develop --port 3000",
14
+ "dist": "cross-env NODE_ENV=production sagui build --optimize",
15
+ "start": "npm run develop",
16
+ "test:coverage": "npm run test:unit -- --coverage",
17
+ "test:lint": "sagui lint",
18
+ "test:unit": "cross-env NODE_ENV=test sagui test",
19
+ "test:unit:watch": "npm run test:unit -- --watch"
20
+ },
21
+ "author": "",
22
+ "license": "MIT",
23
+ "devDependencies": {
24
+ "babel-polyfill": "6.13.0",
25
+ "enzyme": "2.4.1",
26
+ "jasmine-enzyme": "1.2.0",
27
+ "karma-babel-preprocessor": "6.0.1",
28
+ "mappersmith": "0.13.3",
29
+ "material-ui": "0.15.4",
30
+ "moment": "2.15.1",
31
+ "react": "15.3.2",
32
+ "react-addons-test-utils": "15.3.2",
33
+ "react-dom": "15.3.2",
34
+ "react-json-pretty": "1.2.1",
35
+ "react-redux": "4.4.5",
36
+ "react-router": "2.8.1",
37
+ "react-router-redux": "4.0.6",
38
+ "react-tap-event-plugin": "1.0.0",
39
+ "redux": "3.6.0",
40
+ "redux-mock-store": "1.2.1",
41
+ "redux-thunk": "2.1.0",
42
+ "sagui": "6.6.1",
43
+ "webpack-inject-css-loader": "0.1.0"
44
+ }
45
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Sagui configuration object
3
+ * see: http://sagui.js.org/
4
+ */
5
+ const { join } = require('path')
6
+ const env = process.env.NODE_ENV
7
+ const output = (env === 'production')
8
+ ? { publicPath: '/assets/', path: join(__dirname, '../assets') }
9
+ : { publicPath: '/' }
10
+
11
+ module.exports = {
12
+ pages: ['index'],
13
+
14
+ style: {
15
+ cssModules: false
16
+ },
17
+
18
+ webpack: {
19
+ output: output,
20
+ module: {
21
+ preLoaders: [
22
+ {
23
+ test: /src\/index\.scss/, // the main scss/css file
24
+ loader: 'webpack-inject-css-loader?appPath=./src&debug=false'
25
+ }
26
+ ]
27
+ },
28
+ externals: {
29
+ 'cheerio': 'window',
30
+ 'react/addons': true,
31
+ 'react/lib/ExecutionEnvironment': true,
32
+ 'react/lib/ReactContext': true
33
+ }
34
+ },
35
+
36
+ develop: {
37
+ proxy: {
38
+ '/api/v1/*': {
39
+ target: 'http://localhost:9292',
40
+ secure: false
41
+ },
42
+ '/configs': {
43
+ target: 'http://localhost:9292',
44
+ secure: false
45
+ },
46
+ '/assets/*': {
47
+ target: 'http://localhost:9292',
48
+ secure: false
49
+ }
50
+ }
51
+ }
52
+ }
@@ -0,0 +1,42 @@
1
+ import API, { parseResponseError } from 'api'
2
+ import { addFlashMessage } from 'actions/flash-messages'
3
+
4
+ import {
5
+ REQUEST_EVENT_DETAILS,
6
+ RECEIVE_EVENT_DETAILS,
7
+ REQUEST_EVENT_DETAILS_FAILED
8
+ } from 'actions'
9
+
10
+ const requestEventDetails = (event) => ({
11
+ type: REQUEST_EVENT_DETAILS,
12
+ event: event
13
+ })
14
+
15
+ const receiveEventDetails = (data) => ({
16
+ type: RECEIVE_EVENT_DETAILS,
17
+ event: data
18
+ })
19
+
20
+ const requestEventDetailsFailed = (event, error) => ({
21
+ type: REQUEST_EVENT_DETAILS_FAILED,
22
+ event: event,
23
+ error
24
+ })
25
+
26
+ export const fetchEventDetails = (event) => (dispatch, getState) => {
27
+ dispatch(requestEventDetails(event))
28
+
29
+ return API.Event
30
+ .findById({ id: event.id })
31
+ .then((response) => dispatch(receiveEventDetails(response.data)))
32
+ .catch((response) => {
33
+ const error = parseResponseError(response)
34
+ return Promise
35
+ .resolve()
36
+ .then(() => dispatch(requestEventDetailsFailed(event, error.message)))
37
+ .then(() => dispatch(addFlashMessage({
38
+ type: 'error',
39
+ text: error.message
40
+ })))
41
+ })
42
+ }
@@ -0,0 +1,71 @@
1
+ import 'babel-polyfill'
2
+ import Mappersmith from 'mappersmith'
3
+ import 'mappersmith/fixtures'
4
+ import configureMockStore from 'redux-mock-store'
5
+ import thunk from 'redux-thunk'
6
+
7
+ const middlewares = [ thunk ]
8
+ const mockStore = configureMockStore(middlewares)
9
+
10
+ import {
11
+ REQUEST_EVENT_DETAILS,
12
+ RECEIVE_EVENT_DETAILS,
13
+ REQUEST_EVENT_DETAILS_FAILED
14
+ } from 'actions'
15
+
16
+ import { fetchEventDetails } from 'actions/event-details'
17
+
18
+ beforeEach(() => {
19
+ Mappersmith.Env.Fixture.clear()
20
+ })
21
+
22
+ describe('actions/event-details', () => {
23
+ describe('#fetchEventDetails', () => {
24
+ describe('when it succeeds', () => {
25
+ let event, store
26
+ beforeEach(() => {
27
+ event = { id: 1 }
28
+ store = mockStore({})
29
+ Mappersmith.Env.Fixture
30
+ .define('get')
31
+ .matching({ url: `/api/v1/events/${event.id}` })
32
+ .response(event)
33
+ })
34
+
35
+ it('creates REQUEST and RECEIVE actions', (done) => {
36
+ store.dispatch(fetchEventDetails(event)).then(() => {
37
+ const actions = store.getActions()
38
+ expect(actions[0]).toEqual({ type: REQUEST_EVENT_DETAILS, event })
39
+ expect(actions[1]).toEqual({ type: RECEIVE_EVENT_DETAILS, event })
40
+ done()
41
+ })
42
+ .catch((e) => done.fail(`test failed with promise error: ${e.message}`))
43
+ })
44
+ })
45
+
46
+ describe('when it fails', () => {
47
+ it('creates REQUEST and REQUEST_FAILED actions', (done) => {
48
+ const event = { id: 1 }
49
+ const store = mockStore({})
50
+ Mappersmith.Env.Fixture
51
+ .define('get')
52
+ .matching({ url: `/api/v1/events/${event.id}` })
53
+ .failure()
54
+ .response({
55
+ responseText: JSON.stringify({
56
+ error: true,
57
+ message: 'some error'
58
+ })
59
+ })
60
+
61
+ store.dispatch(fetchEventDetails(event)).then(() => {
62
+ const actions = store.getActions()
63
+ expect(actions[0]).toEqual({ type: REQUEST_EVENT_DETAILS, event })
64
+ expect(actions[1]).toEqual({ type: REQUEST_EVENT_DETAILS_FAILED, event, error: 'some error' })
65
+ done()
66
+ })
67
+ .catch((e) => done.fail(`test failed with promise error: ${e.message}`))
68
+ })
69
+ })
70
+ })
71
+ })
@@ -0,0 +1,11 @@
1
+ import { EVENT_SHOW_OVERVIEW, EVENT_HIDE_OVERVIEW } from 'actions'
2
+
3
+ export const showEventOverview = (event) => ({
4
+ type: EVENT_SHOW_OVERVIEW,
5
+ event
6
+ })
7
+
8
+ export const hideEventOverview = (event) => ({
9
+ type: EVENT_HIDE_OVERVIEW,
10
+ event
11
+ })
@@ -0,0 +1,20 @@
1
+ import { EVENT_SHOW_OVERVIEW, EVENT_HIDE_OVERVIEW } from 'actions'
2
+ import { showEventOverview, hideEventOverview } from 'actions/event-overview'
3
+
4
+ describe('actions/event-overview', () => {
5
+ describe('#showEventOverview', () => {
6
+ it('creates an action to show event overview', () => {
7
+ const event = { id: 1 }
8
+ const expectedAction = { type: EVENT_SHOW_OVERVIEW, event: event }
9
+ expect(showEventOverview(event)).toEqual(expectedAction)
10
+ })
11
+ })
12
+
13
+ describe('#hideEventOverview', () => {
14
+ it('creates an action to hide event overview', () => {
15
+ const event = { id: 1 }
16
+ const expectedAction = { type: EVENT_HIDE_OVERVIEW, event: event }
17
+ expect(hideEventOverview(event)).toEqual(expectedAction)
18
+ })
19
+ })
20
+ })
@@ -0,0 +1,58 @@
1
+ import API, { parseResponseError } from 'api'
2
+ import { addFlashMessage } from 'actions/flash-messages'
3
+
4
+ import {
5
+ EVENT_SHOW_RETRY,
6
+ EVENT_HIDE_RETRY,
7
+ REQUEST_EVENT_RETRY,
8
+ RECEIVE_EVENT_RETRY,
9
+ REQUEST_EVENT_RETRY_FAILED
10
+ } from 'actions'
11
+
12
+ export const showEventRetry = (event) => ({
13
+ type: EVENT_SHOW_RETRY,
14
+ event
15
+ })
16
+
17
+ export const hideEventRetry = (event) => ({
18
+ type: EVENT_HIDE_RETRY,
19
+ event
20
+ })
21
+
22
+ const requestEventRetry = (event) => ({
23
+ type: REQUEST_EVENT_RETRY,
24
+ event: event
25
+ })
26
+
27
+ const receiveEventRetry = (event, data) => ({
28
+ type: RECEIVE_EVENT_RETRY,
29
+ event: event,
30
+ acknowledged: data.acknowledged
31
+ })
32
+
33
+ const requestEventRetryFailed = (event, error) => ({
34
+ type: REQUEST_EVENT_RETRY_FAILED,
35
+ event: event,
36
+ error
37
+ })
38
+
39
+ export const performEventRetry = (event) => (dispatch, getState) => {
40
+ dispatch(requestEventRetry(event))
41
+ return API.Event
42
+ .retry({id: event.id})
43
+ .then((response) => {
44
+ return Promise
45
+ .resolve()
46
+ .then(() => dispatch(receiveEventRetry(event, response.data)))
47
+ .then(() => dispatch(hideEventRetry(event)))
48
+ .then(() => dispatch(addFlashMessage({
49
+ type: 'success',
50
+ text: `Event retried with success. Acknowledged: ${response.data.acknowledged}`,
51
+ autoClose: true
52
+ })))
53
+ })
54
+ .catch((response) => {
55
+ const error = parseResponseError(response)
56
+ dispatch(requestEventRetryFailed(event, error.message))
57
+ })
58
+ }
@@ -0,0 +1,117 @@
1
+ import 'babel-polyfill'
2
+ import Mappersmith from 'mappersmith'
3
+ import 'mappersmith/fixtures'
4
+ import configureMockStore from 'redux-mock-store'
5
+ import thunk from 'redux-thunk'
6
+
7
+ const middlewares = [ thunk ]
8
+ const mockStore = configureMockStore(middlewares)
9
+
10
+ import {
11
+ EVENT_SHOW_RETRY,
12
+ EVENT_HIDE_RETRY,
13
+ REQUEST_EVENT_RETRY,
14
+ RECEIVE_EVENT_RETRY,
15
+ ADD_FLASH_MESSAGE,
16
+ REQUEST_EVENT_RETRY_FAILED
17
+ } from 'actions'
18
+
19
+ import {
20
+ showEventRetry,
21
+ hideEventRetry,
22
+ performEventRetry
23
+ } from 'actions/event-retry'
24
+
25
+ beforeEach(() => {
26
+ Mappersmith.Env.Fixture.clear()
27
+ })
28
+
29
+ describe('actions/event-retry', () => {
30
+ describe('#showEventRetry', () => {
31
+ it('creates an action to show event retry', () => {
32
+ const event = { id: 1 }
33
+ const expectedAction = { type: EVENT_SHOW_RETRY, event }
34
+ expect(showEventRetry(event)).toEqual(expectedAction)
35
+ })
36
+ })
37
+
38
+ describe('#hideEventRetry', () => {
39
+ it('creates an action to hide event retry', () => {
40
+ const event = { id: 1 }
41
+ const expectedAction = { type: EVENT_HIDE_RETRY, event }
42
+ expect(hideEventRetry(event)).toEqual(expectedAction)
43
+ })
44
+ })
45
+
46
+ describe('#performEventRetry', () => {
47
+ describe('when it succeeds', () => {
48
+ let event, store
49
+ beforeEach(() => {
50
+ event = { id: 1 }
51
+ store = mockStore({})
52
+ Mappersmith.Env.Fixture
53
+ .define('post')
54
+ .matching({ url: `/api/v1/events/${event.id}/retry` })
55
+ .response({ acknowledged: true })
56
+ })
57
+
58
+ it('creates REQUEST and RECEIVE actions', (done) => {
59
+ store.dispatch(performEventRetry(event)).then(() => {
60
+ const actions = store.getActions()
61
+ expect(actions[0]).toEqual({ type: REQUEST_EVENT_RETRY, event })
62
+ expect(actions[1]).toEqual({ type: RECEIVE_EVENT_RETRY, event, acknowledged: true })
63
+ done()
64
+ })
65
+ .catch((e) => done.fail(`test failed with promise error: ${e.message}`))
66
+ })
67
+
68
+ it('creates an action to hide the event retry', (done) => {
69
+ store.dispatch(performEventRetry(event)).then(() => {
70
+ const actions = store.getActions()
71
+ expect(actions[2]).toEqual({ type: EVENT_HIDE_RETRY, event })
72
+ done()
73
+ })
74
+ .catch((e) => done.fail(`test failed with promise error: ${e.message}`))
75
+ })
76
+
77
+ it('create an action to add a success flash message', (done) => {
78
+ store.dispatch(performEventRetry(event)).then(() => {
79
+ const actions = store.getActions()
80
+ expect(actions[3]).toEqual({ type: ADD_FLASH_MESSAGE, message: {
81
+ id: jasmine.any(String),
82
+ type: 'success',
83
+ text: 'Event retried with success. Acknowledged: true',
84
+ autoClose: true
85
+ }})
86
+ done()
87
+ })
88
+ .catch((e) => done.fail(`test failed with promise error: ${e.message}`))
89
+ })
90
+ })
91
+
92
+ describe('when it fails', () => {
93
+ it('creates REQUEST and REQUEST_FAILED actions', (done) => {
94
+ const event = { id: 1 }
95
+ const store = mockStore({})
96
+ Mappersmith.Env.Fixture
97
+ .define('post')
98
+ .matching({ url: `/api/v1/events/${event.id}/retry` })
99
+ .failure()
100
+ .response({
101
+ responseText: JSON.stringify({
102
+ error: true,
103
+ message: 'some error'
104
+ })
105
+ })
106
+
107
+ store.dispatch(performEventRetry(event)).then(() => {
108
+ const actions = store.getActions()
109
+ expect(actions[0]).toEqual({ type: REQUEST_EVENT_RETRY, event })
110
+ expect(actions[1]).toEqual({ type: REQUEST_EVENT_RETRY_FAILED, event, error: 'some error' })
111
+ done()
112
+ })
113
+ .catch((e) => done.fail(`test failed with promise error: ${e.message}`))
114
+ })
115
+ })
116
+ })
117
+ })
@@ -0,0 +1,75 @@
1
+ import API, { EVENTS_SEARCH_LIMIT, parseResponseError } from 'api'
2
+ import { addFlashMessage } from 'actions/flash-messages'
3
+ import { history } from 'routes'
4
+
5
+ import {
6
+ TRIGGER_SEARCH,
7
+ REQUEST_SEARCH_RESULTS,
8
+ RECEIVE_SEARCH_RESULTS,
9
+ REQUEST_SEARCH_RESULTS_FAILED,
10
+ LOAD_MORE_SEARCH_RESULTS
11
+ } from 'actions'
12
+
13
+ export const triggerSearch = () => (dispatch, getState) => {
14
+ const filters = getState().eventsFilters
15
+ return Promise
16
+ .resolve()
17
+ .then(() => history.push({ query: filters.value ? filters : {} }))
18
+ .then(() => dispatch({ type: TRIGGER_SEARCH }))
19
+ .then(() => dispatch(fetchSearchResults()))
20
+ }
21
+
22
+ const requestSearchResults = () => ({
23
+ type: REQUEST_SEARCH_RESULTS
24
+ })
25
+
26
+ const receiveSearchResults = (data, offset) => ({
27
+ type: RECEIVE_SEARCH_RESULTS,
28
+ events: data,
29
+ offset
30
+ })
31
+
32
+ const requestSearchResultsFailed = (query, error) => ({
33
+ type: REQUEST_SEARCH_RESULTS_FAILED,
34
+ query,
35
+ error
36
+ })
37
+
38
+ export const fetchSearchResults = () => (dispatch, getState) => {
39
+ dispatch(requestSearchResults())
40
+
41
+ const filter = getState().eventsFilters
42
+ const currentOffset = getState().xhrStatus.currentEventsOffset
43
+ const query = filter.value
44
+ ? {[filter.type]: filter.value}
45
+ : {}
46
+
47
+ Object.assign(query, { offset: currentOffset })
48
+
49
+ return API.Event
50
+ .search(query)
51
+ .then((response) => {
52
+ dispatch(receiveSearchResults(response.data, currentOffset))
53
+ })
54
+ .catch((response) => {
55
+ const error = parseResponseError(response)
56
+ return Promise
57
+ .resolve()
58
+ .then(() => dispatch(requestSearchResultsFailed(query, error.message)))
59
+ .then(() => dispatch(addFlashMessage({
60
+ type: 'error',
61
+ text: `Events search failed. "${error.message}"`
62
+ })))
63
+ })
64
+ }
65
+
66
+ export const loadMoreSearchResults = () => (dispatch, getState) => {
67
+ const currentOffset = getState().xhrStatus.currentEventsOffset
68
+ return Promise
69
+ .resolve()
70
+ .then(() => dispatch({
71
+ type: LOAD_MORE_SEARCH_RESULTS,
72
+ offset: currentOffset + EVENTS_SEARCH_LIMIT
73
+ }))
74
+ .then(() => dispatch(fetchSearchResults()))
75
+ }