Adding polling to our Redux API middleware

PR: https://github.com/theforeman/foreman/pull/7069

API Middleware with polling

current generic API action looks like this:

const some_API_action = payload => ({
  type: API_OPERATIONS.GET,
  key: MY_API_REQUEST_SPECIAL_KEY,
  payload,
});

To use polling, we will need to add the “polling” key:

const some_API_action = payload => ({
  type: API_OPERATIONS.GET,
  key: MY_API_REQUEST_SPECIAL_KEY,
  polling: 3000, /** value in ms, or true which will use the default values. */  
  payload,
});

To stop the polling, we will need to use the “stopPolling” Action from APIActions:

// MyComponent/index.js
import { APIActions } from "../../redux/API";
// import { APIActions } from "foremanReact/redux/API"; in plugins
...
const mapDispatchToProps = dispatch => bindActionCreators( { ...actions, ...APIActions }, dispatch)

Then it will be available in your component:

// MyComponent/MyComponent.js
handlePolling = () => {
  /**use the same key that was used to create the API request with polling.*/
  this.props.stopPolling(MY_API_REQUEST_SPECIAL_KEY) 
}

Instead of adding the APIActions to mapDispatchToProps,
you could also use the redux-hook to fire the action:

// MyComponent/MyComponent.js
import { useDispatch } from 'react-redux'
import { stopPolling } from "../../redux/API/APIActions";
// import { APIActions } from "foremanReact/redux/API/APIActions"; in plugins
...
handlePolling = () => {
  const dispatch = useDispatch()
  /**use the same key that was used to create the API request with polling.*/
  dispatch(stopPolling(MY_API_REQUEST_SPECIAL_KEY))
}
1 Like

Updated:
Continue the work from #7069,
PR is now https://github.com/theforeman/foreman/pull/7157

API Middleware with polling

by using the Intervals Middleware

current generic API action looks like this:

const some_API_action = payload => ({
  type: API_OPERATIONS.GET,
  key: MY_API_REQUEST_SPECIAL_KEY,
  payload,
});

To use polling, we will need to add the “polling” key:

const some_API_action = payload => ({
  type: API_OPERATIONS.GET,
  key: MY_API_REQUEST_SPECIAL_KEY,
  polling: 3000, /** value in ms, or true which will use the default values. */  
  payload,
});

There are several ways to stop the API polling:

We will need to use the “stopInterval” Action from IntervalMiddlware.
“stopInterval” is defined in ‘webpack/assets/javascripts/react_app/redux/middlewares/IntervalMiddleware’
or ‘foremanReact/redux/middlewares/IntervalMiddleware’ for plugins

// MyComponent/MyComponentActions.js
....
import { stopInterval } from '../../middlewares/IntervalMiddleware';
...

export const stopPolling = () => stopInterval(MY_API_REQUEST_SPECIAL_KEY);

Then it will be available in your component:

// MyComponent/MyComponent.js
handlePolling = () => {
  /**use the same key that was used to create the API request with polling.*/
  this.props.stopPolling(MY_API_REQUEST_SPECIAL_KEY) 
}

Another option is to add the action to redux “connect” in the index file through “mapDispatchToProps”:

// MyComponent/index.js
import { stopInterval } from "../../redux/middlewares/IntervalMiddleware";
// import { stopInterval } from "foremanReact/redux/middlewares/IntervalMiddleware"; in plugins
...
const mapDispatchToProps = dispatch => bindActionCreators( { ...actions, stopInterval }, dispatch)

Then it will be available in your component:

// MyComponent/MyComponent.js
handlePolling = () => {
  const { stopInterval } = this.props;
  /**use the same key that was used to create the API request with polling.*/
  stopInterval(MY_API_REQUEST_SPECIAL_KEY) 
}

You could also call it with “useDispatch” hook:

// MyComponent/MyComponent.js
import { useDispatch } from 'react-redux'
import { stopInterval } from "../../redux/middlewares/IntervalMiddleware";
// import { stopInterval } from "foremanReact/redux/middlewares/IntervalMiddleware"; in plugins
...
handlePolling = () => {
  const dispatch = useDispatch()
  /**use the same key that was used to create the API request with polling.*/
  dispatch(stopInterval(MY_API_REQUEST_SPECIAL_KEY))
}
1 Like

My concern is that polling in general is not a mechanism we want to encourage. While currently it is a limitation because we can’t listen yet (we are working on switching to Puma which allows using websockets), I would encourage building a higher level abstraction in Javascript to listen which may be implemented using polling under the hood.

In this PR we are introducing a redux interval-middleware which would also manage running intervals,
such as API polling and it would keep track on all running process and prevent you from doing polling the wrong way or making multiple intervals for the same API.

The interval middleware could be also used for any other need.
when websockets will be introduced in Foreman it might be easier to replace all of the places that use polling through the middleware and switch it under the hood.

I am sharing the same concern with you, IMO in this case It should make the transition to websockets easier once we centrelize the polling implementation to small reusable units.

Thanks for pushing it forward @Ron_Lavi

2 Likes