From 06bf038cc75d518e19c7a3880e3fdaf4bb69941e Mon Sep 17 00:00:00 2001 From: Kevin Schiffer Date: Tue, 30 Jul 2024 22:51:20 +0200 Subject: [PATCH] console: Fix interference of entity prefetching data with store state --- .../console/store/actions/applications.js | 16 +++++ pkg/webui/console/store/actions/devices.js | 17 ++++++ pkg/webui/console/store/actions/gateways.js | 16 +++++ .../store/middleware/logics/applications.js | 3 +- .../store/middleware/logics/devices.js | 2 +- .../store/middleware/logics/gateways.js | 3 +- .../store/middleware/logics/organizations.js | 1 - .../store/middleware/logics/top-entities.js | 12 ++-- .../console/store/reducers/applications.js | 2 + pkg/webui/console/store/reducers/devices.js | 2 + pkg/webui/console/store/reducers/gateways.js | 2 + pkg/webui/lib/store/actions/attach-promise.js | 7 ++- .../lib/store/logics/create-request-logic.js | 58 ++++++++----------- 13 files changed, 92 insertions(+), 49 deletions(-) diff --git a/pkg/webui/console/store/actions/applications.js b/pkg/webui/console/store/actions/applications.js index d1e14e8b80..6dbd5bf699 100644 --- a/pkg/webui/console/store/actions/applications.js +++ b/pkg/webui/console/store/actions/applications.js @@ -141,6 +141,22 @@ export const [ }, ] = createPaginationRequestActions(SHARED_NAME) +export const FETCH_APPS_LIST_BASE = 'FETCH_APPS_LIST' +export const [ + { request: FETCH_APPS_LIST, success: FETCH_APPS_LIST_SUCCESS, failure: FETCH_APPS_LIST_FAILURE }, + { + request: fetchApplicationsList, + success: fetchApplicationsListSuccess, + failure: fetchApplicationsListFailure, + }, +] = createRequestActions( + FETCH_APPS_LIST_BASE, + ({ page, limit, query, order, deleted } = {}) => ({ + params: { page, limit, query, order, deleted }, + }), + (_, selectors = [], options = {}) => ({ selectors, options }), +) + export const GET_APPS_RIGHTS_LIST_BASE = createGetRightsListActionType(SHARED_NAME) export const [ { diff --git a/pkg/webui/console/store/actions/devices.js b/pkg/webui/console/store/actions/devices.js index 43bf3f6dc1..b80cff8e4c 100644 --- a/pkg/webui/console/store/actions/devices.js +++ b/pkg/webui/console/store/actions/devices.js @@ -84,6 +84,23 @@ export const [ { request: getDevicesList, success: getDevicesListSuccess, failure: getDevicesListFailure }, ] = createPaginationByIdRequestActions(SHARED_NAME) +export const FETCH_DEVICES_LIST_BASE = 'FETCH_END_DEVICE_LIST' +export const [ + { + request: FETCH_DEVICES_LIST, + success: FETCH_DEVICES_LIST_SUCCESS, + failure: FETCH_DEVICES_LIST_FAILURE, + }, + { request: fetchDevicesList, success: fetchDevicesListSuccess, failure: fetchDevicesListFailure }, +] = createRequestActions( + FETCH_DEVICES_LIST_BASE, + (id, { page, limit, query, order } = {}) => ({ + id, + params: { page, limit, query, order }, + }), + (id, params, selectors = [], options = {}) => ({ selectors, options }), +) + export const RESET_DEV_BASE = 'RESET_END_DEVICE' export const [ { request: RESET_DEV, success: RESET_DEV_SUCCESS, failure: RESET_DEV_FAILURE }, diff --git a/pkg/webui/console/store/actions/gateways.js b/pkg/webui/console/store/actions/gateways.js index 8d3781e376..c24d00eb1b 100644 --- a/pkg/webui/console/store/actions/gateways.js +++ b/pkg/webui/console/store/actions/gateways.js @@ -100,6 +100,22 @@ export const [ { request: getGatewaysList, success: getGatewaysListSuccess, failure: getGatewaysListFailure }, ] = createPaginationRequestActions(SHARED_NAME) +export const FETCH_GTWS_LIST_BASE = 'FETCH_GTWS_LIST' +export const [ + { request: FETCH_GTWS_LIST, success: FETCH_GTWS_LIST_SUCCESS, failure: FETCH_GTWS_LIST_FAILURE }, + { + request: fetchGatewaysList, + success: fetchGatewaysListSuccess, + failure: fetchGatewaysListFailure, + }, +] = createRequestActions( + FETCH_GTWS_LIST_BASE, + ({ page, limit, query, order, deleted } = {}) => ({ + params: { page, limit, query, order, deleted }, + }), + (_, selectors = [], options = {}) => ({ selectors, options }), +) + export const GET_GTWS_RIGHTS_LIST_BASE = createGetRightsListActionType(SHARED_NAME) export const [ { diff --git a/pkg/webui/console/store/middleware/logics/applications.js b/pkg/webui/console/store/middleware/logics/applications.js index 3ed514b173..2d4de8838b 100644 --- a/pkg/webui/console/store/middleware/logics/applications.js +++ b/pkg/webui/console/store/middleware/logics/applications.js @@ -107,8 +107,7 @@ const restoreApplicationLogic = createRequestLogic({ }) const getApplicationsLogic = createRequestLogic({ - type: applications.GET_APPS_LIST, - latest: true, + type: [applications.GET_APPS_LIST, applications.FETCH_APPS_LIST], process: async ({ action }, dispatch) => { const { params: { page, limit, query, order, deleted }, diff --git a/pkg/webui/console/store/middleware/logics/devices.js b/pkg/webui/console/store/middleware/logics/devices.js index c0df22ff2d..e0cf8f3d54 100644 --- a/pkg/webui/console/store/middleware/logics/devices.js +++ b/pkg/webui/console/store/middleware/logics/devices.js @@ -119,7 +119,7 @@ const deleteDeviceLogic = createRequestLogic({ }) const getDevicesListLogic = createRequestLogic({ - type: devices.GET_DEVICES_LIST, + type: [devices.GET_DEVICES_LIST, devices.FETCH_DEVICES_LIST], process: async ({ action }) => { const { id: appId, diff --git a/pkg/webui/console/store/middleware/logics/gateways.js b/pkg/webui/console/store/middleware/logics/gateways.js index b8665e391a..1be38b5266 100644 --- a/pkg/webui/console/store/middleware/logics/gateways.js +++ b/pkg/webui/console/store/middleware/logics/gateways.js @@ -93,8 +93,7 @@ const restoreGatewayLogic = createRequestLogic({ }) const getGatewaysLogic = createRequestLogic({ - type: gateways.GET_GTWS_LIST, - latest: true, + type: [gateways.GET_GTWS_LIST, gateways.FETCH_GTWS_LIST], process: async ({ action }) => { const { params: { page, limit, query, order, deleted }, diff --git a/pkg/webui/console/store/middleware/logics/organizations.js b/pkg/webui/console/store/middleware/logics/organizations.js index 65fa2968e2..cbc4f75eaa 100644 --- a/pkg/webui/console/store/middleware/logics/organizations.js +++ b/pkg/webui/console/store/middleware/logics/organizations.js @@ -38,7 +38,6 @@ const getOrganizationLogic = createRequestLogic({ const getOrganizationsLogic = createRequestLogic({ type: organizations.GET_ORGS_LIST, - latest: true, process: async ({ action }, dispatch) => { const { params: { page, limit, order, query, deleted }, diff --git a/pkg/webui/console/store/middleware/logics/top-entities.js b/pkg/webui/console/store/middleware/logics/top-entities.js index 2015ac30b7..3db2a6390c 100644 --- a/pkg/webui/console/store/middleware/logics/top-entities.js +++ b/pkg/webui/console/store/middleware/logics/top-entities.js @@ -28,9 +28,9 @@ import { getTypeAndId } from '@console/lib/recency-frequency-entities' import { GET_TOP_ENTITIES } from '@console/store/actions/top-entities' import { getBookmarksList } from '@console/store/actions/user-preferences' -import { getApplication, getApplicationsList } from '@console/store/actions/applications' -import { getGateway, getGatewaysList } from '@console/store/actions/gateways' -import { getDevice, getDevicesList } from '@console/store/actions/devices' +import { fetchApplicationsList, getApplication } from '@console/store/actions/applications' +import { fetchGatewaysList, getGateway } from '@console/store/actions/gateways' +import { fetchDevicesList, getDevice } from '@console/store/actions/devices' import { selectUserId } from '@account/store/selectors/user' import { @@ -134,11 +134,11 @@ const fetchTopEntities = async (getState, dispatch) => { try { await Promise.all([ dispatch( - attachPromise(getApplicationsList({ page: 1, limit: prefetchLimit, order }, ['name'])), + attachPromise(fetchApplicationsList({ page: 1, limit: prefetchLimit, order }, ['name'])), ), dispatch( attachPromise( - getGatewaysList( + fetchGatewaysList( { page: 1, limit: prefetchLimit, order }, ['name', 'gateway_server_address'], { @@ -173,7 +173,7 @@ const fetchTopEntities = async (getState, dispatch) => { try { return await dispatch( attachPromise( - getDevicesList(entityId, { page: 1, limit: 1000, order }, ['name', 'last_seen_at']), + fetchDevicesList(entityId, { page: 1, limit: 1000, order }, ['name', 'last_seen_at']), ), ) } catch (error) { diff --git a/pkg/webui/console/store/reducers/applications.js b/pkg/webui/console/store/reducers/applications.js index 3245087ba6..938ab036fc 100644 --- a/pkg/webui/console/store/reducers/applications.js +++ b/pkg/webui/console/store/reducers/applications.js @@ -27,6 +27,7 @@ import { DELETE_APP_SUCCESS, GET_APP_EVENT_MESSAGE_SUCCESS, GET_MQTT_INFO_SUCCESS, + FETCH_APPS_LIST_SUCCESS, } from '@console/store/actions/applications' const application = (state = {}, application) => ({ @@ -52,6 +53,7 @@ const applications = (state = defaultState, { type, payload, event, meta }) => { selectedApplication: payload.id, } case GET_APPS_LIST_SUCCESS: + case FETCH_APPS_LIST_SUCCESS: const entities = payload.entities.reduce( (acc, app) => { const id = getApplicationId(app) diff --git a/pkg/webui/console/store/reducers/devices.js b/pkg/webui/console/store/reducers/devices.js index 1862956b9e..29b4a76100 100644 --- a/pkg/webui/console/store/reducers/devices.js +++ b/pkg/webui/console/store/reducers/devices.js @@ -30,6 +30,7 @@ import { RESET_DEV_SUCCESS, GET_DEVICE_EVENT_MESSAGE_SUCCESS, DELETE_DEV_SUCCESS, + FETCH_DEVICES_LIST_SUCCESS, } from '@console/store/actions/devices' import { GET_APP_EVENT_MESSAGE_SUCCESS } from '@console/store/actions/applications' @@ -156,6 +157,7 @@ const devices = (state = defaultState, { type, payload, event, meta }) => { } return mergeDerived(state, combinedId, defaultDerived) + case FETCH_DEVICES_LIST_SUCCESS: case GET_DEVICES_LIST_SUCCESS: const newEntities = payload.entities.reduce( (acc, dev) => { diff --git a/pkg/webui/console/store/reducers/gateways.js b/pkg/webui/console/store/reducers/gateways.js index 2ccb14c557..cf80a34173 100644 --- a/pkg/webui/console/store/reducers/gateways.js +++ b/pkg/webui/console/store/reducers/gateways.js @@ -26,6 +26,7 @@ import { UPDATE_GTW_STATS_FAILURE, START_GTW_STATS_SUCCESS, START_GTW_STATS_FAILURE, + FETCH_GTWS_LIST_SUCCESS, } from '@console/store/actions/gateways' const defaultStatisticsState = { @@ -123,6 +124,7 @@ const gateways = (state = defaultState, action) => { selectedGateway: null, entities: rest, } + case FETCH_GTWS_LIST_SUCCESS: case GET_GTWS_LIST_SUCCESS: const entities = payload.entities.reduce( (acc, gtw) => { diff --git a/pkg/webui/lib/store/actions/attach-promise.js b/pkg/webui/lib/store/actions/attach-promise.js index 83b9715584..066b539cb6 100644 --- a/pkg/webui/lib/store/actions/attach-promise.js +++ b/pkg/webui/lib/store/actions/attach-promise.js @@ -68,8 +68,11 @@ export default actionOrActionCreator => { * Helper function to retrieve the result action types based * on the request action type. * - * @param {string} typeString - The request action type. + * @param {string|Array} type - The request action type. * @param {string} status - The result type, either `SUCCESS`, `FAILURE` or `ABORT`. * @returns {string} - The result action type. */ -export const getResultActionFromType = (typeString, status) => typeString.replace('REQUEST', status) +export const getResultActionFromType = (type, status) => + type instanceof Array + ? type.map(t => t.replace('REQUEST', status)) + : type.replace('REQUEST', status) diff --git a/pkg/webui/lib/store/logics/create-request-logic.js b/pkg/webui/lib/store/logics/create-request-logic.js index d3097a7013..c5ab69cbaf 100644 --- a/pkg/webui/lib/store/logics/create-request-logic.js +++ b/pkg/webui/lib/store/logics/create-request-logic.js @@ -1,4 +1,4 @@ -// Copyright © 2019 The Things Network Foundation, The Things Industries B.V. +// Copyright © 2024 The Things Network Foundation, The Things Industries B.V. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -48,58 +48,46 @@ let lastError * @param {object} options - The logic options (to be passed to `createLogic()`). * @param {boolean} options.noCancelOnRouteChange - Flag to disable the decoration * of the `cancelType` option. - * @param {(string|Function)} successType - The success action type or action creator. - * @param {(string|Function)} failType - The fail action type or action creator. - * @param {(string|Function)} abortType - The abort action type or action creator. + * @param {string} abortType - The type of the abort action. * @returns {object} The `redux-logic` (decorated) logic. */ const createRequestLogic = ( { noCancelOnRouteChange, ...options }, - successType = getResultActionFromType(options.type, 'SUCCESS'), - failType = getResultActionFromType(options.type, 'FAILURE'), abortType = getResultActionFromType(options.type, 'ABORT'), ) => { - if (!successType || !failType) { + if ( + options.type === undefined || + (options.type instanceof Array && options.type.length === 0) || + (options.type instanceof Array && + options.type.some(type => typeof type !== 'string' || type.indexOf('_REQUEST') === -1)) + ) { throw new Error('Could not derive result actions from provided options') } - - let successAction = successType - let failAction = failType - let abortAction = abortType - - if (typeof successType === 'string') { - successAction = payload => ({ type: successType, payload }) - } - if (typeof failType === 'string') { - failAction = (error, originalAction) => ({ - type: failType, - error: true, - payload: error, - meta: { requestPayload: originalAction.payload }, - }) - } - if (typeof abortType === 'string') { - abortAction = () => ({ type: abortType }) - } - return createLogic({ ...options, cancelType: abortType, process: async (deps, dispatch, done) => { - const { action, getState, cancelled$, action$ } = deps + const { action, getState, cancelled$ } = deps const promiseAttached = action.meta && action.meta._attachPromise const promisifiedDispatch = promisifyDispatch(dispatch) let actionSubscription + // Compose response actions. + const successAction = payload => ({ + type: getResultActionFromType(action.type, 'SUCCESS'), + payload, + }) + const failAction = (error, originalAction) => ({ + type: getResultActionFromType(action.type, 'FAILURE'), + error: true, + payload: error, + meta: { requestPayload: originalAction.payload }, + }) + const abortAction = () => ({ type: getResultActionFromType(action.type, 'ABORT') }) + if (!noCancelOnRouteChange) { // Subscribe to action observable to dispatch an abort action on route changes. - actionSubscription = action$.subscribe({ - next: action => { - if (action.type === '@@router/LOCATION_CHANGE') { - dispatch(abortAction()) - } - }, - }) + // TODO: Reintroduce the route change detection after removing the `connected-react-router`. } let success = false