1
1
import { fetch } from './fetch' ;
2
2
import {
3
- ONE , MANY , DELETE , POST , PUT , generateDefaultStateFull , isPlural ,
3
+ ONE , MANY , DELETE , POST , PUT ,
4
4
} from './internal' ;
5
5
6
6
@@ -10,60 +10,6 @@ export function fullyLoadedObject(object) {
10
10
return object && ! ! Object . keys ( object ) . filter ( key => ! key . startsWith ( '_' ) ) . length ;
11
11
}
12
12
13
- /*
14
- * This function applies the ids to the config to try to find
15
- * the particular object we are talking about.
16
- *
17
- * Examples:
18
- * getStateOfSpecificResource(require('~/api/generic/linodes').config.generic,
19
- * store.api, ['1234'])
20
- * returns store.api.linodes.linodes['1234']._generic.generic
21
- *
22
- * getStateOfSpecificResource(require('~/api/generic/linodes').config.generic,
23
- * store.api, ['1234', '1'])
24
- * returns store.api.linodes.linodes['1234']._generic.generic['1']
25
- */
26
- export function getStateOfSpecificResource ( config , state , ids ) {
27
- const path = [ ] ;
28
- let root = config ;
29
- const match = ( sub , parent ) => {
30
- if ( parent . subresources [ sub ] === root ) {
31
- path . push ( sub ) ;
32
- }
33
- } ;
34
- while ( root . parent ) {
35
- const parent = root . parent ;
36
- Object . keys ( parent . subresources ) . forEach ( s => match ( s , parent ) ) ;
37
- root = parent ;
38
- }
39
- let refined = state . api [ root . name ] ;
40
- const _ids = [ ...ids ] ;
41
- let current = root ;
42
- let name = null ;
43
-
44
- while ( current !== config ) {
45
- name = path . pop ( ) ;
46
- if ( isPlural ( current ) ) {
47
- refined = refined [ current . name ] [ _ids . shift ( ) ] [ name ] ;
48
- } else {
49
- // Not totally sure how things would work with a plural inside a singular
50
- refined = refined [ current . name ] [ name ] ;
51
- }
52
- current = current . subresources [ name ] ;
53
- }
54
-
55
- if ( _ids . length ) {
56
- // Should only be a plural one anyway, but just in case.
57
- const objects = refined [ current . name ] ;
58
- if ( ! isPlural ( current ) ) {
59
- return objects ;
60
- }
61
-
62
- return objects [ _ids [ _ids . length - 1 ] ] ;
63
- }
64
- return refined ;
65
- }
66
-
67
13
/*
68
14
* Apply a filter to all returned objects so only selected fields (or none)
69
15
* will be updated.
@@ -109,119 +55,46 @@ function genThunkOne(config, actions) {
109
55
* The results are returned.
110
56
*/
111
57
function genThunkPage ( config , actions ) {
112
- function fetchPage ( page = 0 , ids = [ ] , resourceFilter , storeInState = true ,
113
- fetchBeganAt , headers ) {
114
- return async ( dispatch , getState ) => {
58
+ function fetchPage ( page = 0 , ids = [ ] , resourceFilter , headers ) {
59
+ return async ( dispatch ) => {
115
60
const endpoint = `${ config . endpoint ( ...ids , '' ) } ?page=${ page + 1 } ` ;
116
-
117
61
const resources = await dispatch ( fetch . get ( endpoint , undefined , headers ) ) ;
118
62
resources [ config . name ] = resources . data || [ ] ;
119
-
120
- const now = fetchBeganAt || new Date ( ) ;
121
-
122
- // The filterResources function must acknowledge that it may not be getting
123
- // the most up-to-date results.
124
63
const filteredResources = filterResources ( config , resources , resourceFilter ) ;
125
-
126
- // Refetch any existing results that have been updated since fetchBeganAt.
127
- const fetchOne = genThunkOne ( config , actions ) ;
128
- const objects = await Promise . all ( filteredResources [ config . name ] . map ( async ( resource ) => {
129
- const existingResourceState = getStateOfSpecificResource (
130
- config , getState ( ) , [ ...ids , resource . id ] ) ;
131
- if ( existingResourceState ) {
132
- const hasActualState = fullyLoadedObject ( existingResourceState ) ;
133
-
134
- const updatedAt = hasActualState && existingResourceState . __updatedAt || fetchBeganAt ;
135
- if ( updatedAt > now ) {
136
- try {
137
- return await dispatch ( fetchOne ( [ ...ids , resource . id ] ) ) ;
138
- } catch ( e ) {
139
- // There's some case where fetching will 404 because there's some item in internal
140
- // state that is not returned from the API (or was because the API is returning
141
- // deleted items?). Catch that and don't blow up; log for good measure.
142
- // eslint-disable-next-line
143
- console . trace ( e ) ;
144
- return Promise . resolve ( ) ;
145
- }
146
- }
147
- }
148
- return resource ;
149
- } ) ) ;
150
-
151
- const updatedResources = {
152
- ...filteredResources ,
153
- [ config . name ] : objects ,
154
- } ;
155
-
156
- if ( storeInState ) {
157
- await dispatch ( actions . many ( updatedResources , ...ids ) ) ;
158
- }
159
-
160
- return updatedResources ;
64
+ await dispatch ( actions . many ( filteredResources , ...ids ) ) ;
65
+ return filteredResources ;
161
66
} ;
162
67
}
163
68
164
69
return fetchPage ;
165
70
}
166
71
167
72
/*
168
- * This function fetches all pages. If the final page indicates the total results
169
- * were not fetched, it restarts the process. If it is fixing partial invalidation,
170
- * it waits to invalidate and store the new data into the store until all the
171
- * pages have been fetched.
73
+ * This function fetches all pages, stores them in Redux and returns the result
172
74
*/
173
75
function genThunkAll ( config , actions , fetchPage ) {
174
76
function fetchAll ( ids = [ ] , resourceFilter , options ) {
175
- return async ( dispatch , getState ) => {
176
- let state = getStateOfSpecificResource ( config , getState ( ) , ids ) ||
177
- generateDefaultStateFull ( config ) ;
178
-
179
- const fetchBeganAt = new Date ( ) ;
180
-
77
+ return async ( dispatch ) => {
181
78
// Grab first page so we know how many there are.
182
- const storeInState = ! state . invalid ; // Store the fetched results later
183
79
const resource = await dispatch (
184
- fetchPage ( 0 , ids , resourceFilter , storeInState , fetchBeganAt , options ) ) ;
80
+ fetchPage ( 0 , ids , resourceFilter , options ) ) ;
185
81
const resources = [ resource ] ;
186
- state = getStateOfSpecificResource ( config , getState ( ) , ids ) ;
187
82
188
- // Grab all pages we know about. If state.invalid, don't save the result
189
- // in the redux store until we've got all the results.
83
+ // Grab all pages we know about and store them in Redux.
190
84
const requests = [ ] ;
191
85
for ( let i = 1 ; i < resources [ 0 ] . pages ; i += 1 ) {
192
- requests . push ( fetchPage ( i , ids , resourceFilter , ! state . invalid , fetchBeganAt , options ) ) ;
86
+ requests . push ( fetchPage ( i , ids , resourceFilter , options ) ) ;
193
87
}
194
88
89
+ // Gather all the results for for return to the caller
195
90
const allPages = await Promise . all ( requests . map ( r => dispatch ( r ) ) ) ;
196
91
allPages . forEach ( function ( response ) {
197
92
resources . push ( response ) ;
198
93
} ) ;
199
-
200
- // If the number of total results returned by the last page is different
201
- // than the total number of results we have, restart.
202
- const numFetchedResources = resources . map (
203
- resource => resource [ config . name ] . length
204
- ) . reduce ( ( a , b ) => a + b ) ;
205
- const numExpectedResources = resources [ resources . length - 1 ] . results ;
206
- if ( numFetchedResources !== numExpectedResources ) {
207
- return await dispatch ( fetchAll ( ids , resourceFilter ) ) ;
208
- }
209
-
210
- // Waits until all things have been fetched so we don't have UI flashes
211
- // while we wait for requests to be made and the results saved in the redux store.
212
- if ( state . invalid ) {
213
- dispatch ( actions . invalidate ( ) ) ;
214
-
215
- await Promise . all ( resources . map ( page =>
216
- dispatch ( actions . many ( page , ...ids ) ) ) ) ;
217
- }
218
-
219
- // The resulting object will look like this, return it so anyone can use it immediately.
220
94
const res = {
221
95
...resources [ resources . length - 1 ] ,
222
96
[ config . name ] : resources . reduce ( ( a , b ) => [ ...a , ...b [ config . name ] ] , [ ] ) ,
223
97
} ;
224
-
225
98
return res ;
226
99
} ;
227
100
}
0 commit comments