Skip to content

Commit

Permalink
basic edits +
Browse files Browse the repository at this point in the history
Also make error logic consistent in readingsApi.ts
  • Loading branch information
huss committed Feb 14, 2024
1 parent 6c74c28 commit ca5ad29
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 68 deletions.
5 changes: 2 additions & 3 deletions src/client/app/redux/actions/meters.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// TODO Marked For Deletion after RTK migration solidified
/* eslint-disable jsdoc/check-param-names */
Expand Down
8 changes: 4 additions & 4 deletions src/client/app/redux/api/baseApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ export const baseApi = createApi({
// For each api call attempt to set the JWT token in the request header
// Token placed in store either on startup after validation, or via credentialed login
if (state.currentUser.token) {
headers.set('token', state.currentUser.token)
headers.set('token', state.currentUser.token);
}
},
// Default Behavior assumes all response are json
// use content type cause API responses are varied
// Default Behavior assumes all responses are json
// use content type because API responses are varied
// TODO Validate Behavior against all endpoints
responseHandler: 'content-type'
}),
Expand All @@ -39,4 +39,4 @@ export const baseApi = createApi({
endpoints: () => ({})
// Defaults to 60 seconds or 1 minute
// keepUnusedDataFor: 60
})
})
8 changes: 6 additions & 2 deletions src/client/app/redux/api/conversionsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const conversionsApi = baseApi.injectEndpoints({
conversionsApi.endpoints.refresh.initiate({
redoCik: true,
refreshReadingViews: false
}))
}));
})
}

Expand All @@ -46,7 +46,11 @@ export const conversionsApi = baseApi.injectEndpoints({
// TODO Verify Behavior w/ Maintainers
queryFulfilled
.then(() => {
dispatch(conversionsApi.endpoints.refresh.initiate({ redoCik: true, refreshReadingViews: false }))
dispatch(conversionsApi.endpoints.refresh.initiate(
{
redoCik: true,
refreshReadingViews: false
}));
})

}
Expand Down
8 changes: 4 additions & 4 deletions src/client/app/redux/api/groupsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import { GroupChildren, GroupData } from '../../types/redux/groups';
import { baseApi } from './baseApi';
export const groupsAdapter = createEntityAdapter<GroupData>({
sortComparer: (groupA, groupB) => groupA.name?.localeCompare(groupB.name, undefined, { sensitivity: 'accent' })
})
export const groupsInitialState = groupsAdapter.getInitialState()
export type GroupDataState = EntityState<GroupData, number>
});
export const groupsInitialState = groupsAdapter.getInitialState();
export type GroupDataState = EntityState<GroupData, number>;

export const groupsApi = baseApi.injectEndpoints({
endpoints: builder => ({
Expand Down Expand Up @@ -100,4 +100,4 @@ export const {
export const selectGroupNameWithID = (state: RootState, groupId: number) => {
const groupInfo = selectGroupById(state, groupId)
return groupInfo ? groupInfo.name : '';
}
}
13 changes: 6 additions & 7 deletions src/client/app/redux/api/metersApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export const meterAdapter = createEntityAdapter<MeterData>({
sortComparer: (meterA, meterB) => meterA.identifier?.localeCompare(meterB.identifier, undefined, { sensitivity: 'accent' })

})
export const metersInitialState = meterAdapter.getInitialState()
export type MeterDataState = EntityState<MeterData, number>
export const metersInitialState = meterAdapter.getInitialState();
export type MeterDataState = EntityState<MeterData, number>;

export const metersApi = baseApi.injectEndpoints({
endpoints: builder => ({
Expand Down Expand Up @@ -76,16 +76,15 @@ export const metersApi = baseApi.injectEndpoints({
})


export const selectMeterDataResult = metersApi.endpoints.getMeters.select()
export const selectMeterDataResult = metersApi.endpoints.getMeters.select();

export const {
selectAll: selectAllMeters,
selectById: selectMeterById,
selectTotal: selectMeterTotal,
selectIds: selectMeterIds,
selectEntities: selectMeterDataById
} = meterAdapter.getSelectors((state: RootState) => selectMeterDataResult(state).data ?? metersInitialState)

} = meterAdapter.getSelectors((state: RootState) => selectMeterDataResult(state).data ?? metersInitialState);

/**
* Selects the name of the meter associated with a given meter ID from the Redux state.
Expand All @@ -98,7 +97,7 @@ export const {
export const selectMeterNameById = (state: RootState, meterID: number) => {
const meterInfo = selectMeterById(state, meterID);
return meterInfo ? meterInfo.name : '';
}
};

/**
* Selects the identifier (not the meter ID) of the meter associated with a given meter ID from the Redux state.
Expand All @@ -111,4 +110,4 @@ export const selectMeterNameById = (state: RootState, meterID: number) => {
export const selectMeterIdentifierById = (state: RootState, meterID: number) => {
const meterInfo = selectMeterById(state, meterID);
return meterInfo ? meterInfo.identifier : '';
}
};
83 changes: 39 additions & 44 deletions src/client/app/redux/api/readingsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const readingsApi = baseApi.injectEndpoints({
// an entry for each means requesting the same data again for ALL meters. which results in too much duplicate data requests

// We keep all args other than the ids.
return _.omit(queryArgs, 'ids')
return _.omit(queryArgs, 'ids');
},
merge: (currentCacheData, responseData) => {
// By default subsequent queries that resolve to the same cache entry will overwrite the existing data.
Expand All @@ -45,54 +45,49 @@ export const readingsApi = baseApi.injectEndpoints({
// Since this is wrapped with Immer, you may either mutate the currentCacheValue directly, or return a new value, but not both at once.

// mutate current cache draft
Object.assign(currentCacheData, responseData)
Object.assign(currentCacheData, responseData);
},
forceRefetch: ({ currentArg, endpointState }) => {
// Since we modified the way the we serialize the args any subsequent query would return the cache data, even if new meters were requested
// To resolve this we provide a forceRefetch where we decide if data needs to be fetched, or retrieved from the cache.

// get existing cached Keys if any
const currentData = endpointState?.data ? Object.keys(endpointState.data).map(Number) : []
const currentData = endpointState?.data ? Object.keys(endpointState.data).map(Number) : [];

// check if the ALL requested id's already exist in cache
const dataInCache = currentArg?.ids.every(id => currentData.includes(id))
const dataInCache = currentArg?.ids.every(id => currentData.includes(id));

// if data requested already lives in the cache, no fetch necessary, else fetch for data
return !dataInCache
return !dataInCache;
},
queryFn: async (args, queryApi, _extra, baseQuery) => {
// We opt for a query function here instead of the normal query: args => {....}
// In a queryFn, we can reference the store's state, to manipulate the provided query args
// The query can request multiple ids, but we may already have some data cached, so only request the necessary ids.

// use the query api to get the store's state, (Type Assertion necessary for typescript otherwise, 'unknown')
const state = queryApi.getState() as RootState
const state = queryApi.getState() as RootState;
// get cache data utilizing the readings Api endpoint
// Refer to: https://redux-toolkit.js.org/rtk-query/api/created-api/endpoints#select
const cachedData = readingsApi.endpoints.line.select(args)(state).data
const cachedData = readingsApi.endpoints.line.select(args)(state).data;
// map cache keys to a number array, if any
const cachedIDs = cachedData ? Object.keys(cachedData).map(Number) : []
const cachedIDs = cachedData ? Object.keys(cachedData).map(Number) : [];
// get the args provided in the original request
const { ids, timeInterval, graphicUnitId, meterOrGroup } = args
const { ids, timeInterval, graphicUnitId, meterOrGroup } = args;
// subtract any already cached keys from the requested ids, and stringify the array for the url endpoint
const idsToFetch = _.difference(ids, cachedIDs).join(',')
const idsToFetch = _.difference(ids, cachedIDs).join(',');


// use the baseQuery from the queryFn with our url endpoint
const { data, error } = await baseQuery({
// api url from derived request arguments
url: `api/unitReadings/line/${meterOrGroup}/${idsToFetch}`,
params: { timeInterval, graphicUnitId }
})
});

// https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#implementing-a-queryfn
// queryFn requires either a data, or error object to be returned
if (error) {
return { error }
}
// since we define custom merge behavior, incoming data will merge with the existing cache if any
return { data: data as LineReadings }

return error ? { error } : { data: data as LineReadings };
}
}),
bar: builder.query<BarReadings, BarReadingApiArgs>({
Expand All @@ -102,34 +97,34 @@ export const readingsApi = baseApi.injectEndpoints({
forceRefetch: ({ currentArg, endpointState }) => {
const currentData = endpointState?.data ? Object.keys(endpointState.data).map(Number) : [];
const dataInCache = currentArg?.ids.every(id => currentData.includes(id));
return !dataInCache
return !dataInCache;
},
queryFn: async (args, queryApi, _extra, baseQuery) => {
const { ids, meterOrGroup, ...params } = args
const state = queryApi.getState() as RootState
const cachedData = readingsApi.endpoints.bar.select(args)(state).data
const cachedIDs = cachedData ? Object.keys(cachedData).map(Number) : []
const idsToFetch = _.difference(ids, cachedIDs).join(',')
const { data, error } = await baseQuery({ url: `api/unitReadings/bar/${meterOrGroup}/${idsToFetch}`, params })
return error ? { error } : { data: data as BarReadings }
const { ids, meterOrGroup, ...params } = args;
const state = queryApi.getState() as RootState;
const cachedData = readingsApi.endpoints.bar.select(args)(state).data;
const cachedIDs = cachedData ? Object.keys(cachedData).map(Number) : [];
const idsToFetch = _.difference(ids, cachedIDs).join(',');
const { data, error } = await baseQuery({ url: `api/unitReadings/bar/${meterOrGroup}/${idsToFetch}`, params });
return error ? { error } : { data: data as BarReadings };
}
}),
compare: builder.query<CompareReadings, CompareReadingApiArgs>({
serializeQueryArgs: ({ queryArgs }) => _.omit(queryArgs, 'ids'),
merge: (currentCacheData, responseData) => { Object.assign(currentCacheData, responseData) },
forceRefetch: ({ currentArg, endpointState }) => {
const currentData = endpointState?.data ? Object.keys(endpointState.data).map(Number) : []
const requestedAlreadyCached = currentArg?.ids.every(id => currentData.includes(id))
return !requestedAlreadyCached
const currentData = endpointState?.data ? Object.keys(endpointState.data).map(Number) : [];
const requestedAlreadyCached = currentArg?.ids.every(id => currentData.includes(id));
return !requestedAlreadyCached;
},
queryFn: async (args, queryApi, _extra, baseQuery) => {
const { ids, meterOrGroup, ...params } = args
const state = queryApi.getState() as RootState
const cachedData = readingsApi.endpoints.compare.select(args)(state).data
const cachedIDs = cachedData ? Object.keys(cachedData).map(Number) : []
const idsToFetch = _.difference(ids, cachedIDs).join(',')
const { data, error } = await baseQuery({ url: `/api/compareReadings/${meterOrGroup}/${idsToFetch}`, params })
return error ? { error } : { data: data as CompareReadings }
const { ids, meterOrGroup, ...params } = args;
const state = queryApi.getState() as RootState;
const cachedData = readingsApi.endpoints.compare.select(args)(state).data;
const cachedIDs = cachedData ? Object.keys(cachedData).map(Number) : [];
const idsToFetch = _.difference(ids, cachedIDs).join(',');
const { data, error } = await baseQuery({ url: `/api/compareReadings/${meterOrGroup}/${idsToFetch}`, params });
return error ? { error } : { data: data as CompareReadings };
}
}),
radar: builder.query<LineReadings, RadarReadingApiArgs>({
Expand All @@ -139,18 +134,18 @@ export const readingsApi = baseApi.injectEndpoints({
forceRefetch: ({ currentArg, endpointState }) => {
const currentData = endpointState?.data ? Object.keys(endpointState.data).map(Number) : [];
const dataInCache = currentArg?.ids.every(id => currentData.includes(id));
return !dataInCache
return !dataInCache;
},
queryFn: async (args, queryApi, _extra, baseQuery) => {
const { ids, meterOrGroup, ...params } = args
const state = queryApi.getState() as RootState
const cachedData = readingsApi.endpoints.radar.select(args)(state).data
const cachedIDs = cachedData ? Object.keys(cachedData).map(Number) : []
const idsToFetch = _.difference(ids, cachedIDs).join(',')
const { data, error } = await baseQuery({ url: `api/unitReadings/radar/${meterOrGroup}/${idsToFetch}`, params })
return error ? { error } : { data: data as LineReadings }
const { ids, meterOrGroup, ...params } = args;
const state = queryApi.getState() as RootState;
const cachedData = readingsApi.endpoints.radar.select(args)(state).data;
const cachedIDs = cachedData ? Object.keys(cachedData).map(Number) : [];
const idsToFetch = _.difference(ids, cachedIDs).join(',');
const { data, error } = await baseQuery({ url: `api/unitReadings/radar/${meterOrGroup}/${idsToFetch}`, params });
return error ? { error } : { data: data as LineReadings };
}
})

})
})
})
4 changes: 2 additions & 2 deletions src/client/app/redux/api/unitsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ export const unitsApi = baseApi.injectEndpoints({
})
})

export const selectUnitDataResult = unitsApi.endpoints.getUnitsDetails.select()
export const selectUnitDataResult = unitsApi.endpoints.getUnitsDetails.select();
export const {
selectAll: selectAllUnits,
selectById: selectUnitById,
selectTotal: selectUnitTotal,
selectIds: selectUnitIds,
selectEntities: selectUnitDataById
} = unitsAdapter.getSelectors((state: RootState) => selectUnitDataResult(state).data ?? unitsInitialState)
} = unitsAdapter.getSelectors((state: RootState) => selectUnitDataResult(state).data ?? unitsInitialState);

4 changes: 2 additions & 2 deletions src/client/app/redux/api/versionApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ export const versionApi = baseApi.injectEndpoints({
})
})

export const selectVersion = versionApi.endpoints.getVersion.select()
export const selectVersion = versionApi.endpoints.getVersion.select();
export const selectOEDVersion = createSelector(
selectVersion,
({ data: version }) => {
return version ?? ''
}
)
);

0 comments on commit ca5ad29

Please sign in to comment.