phobos_checkpoint_ui 0.1.0

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 (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
+ }