InstanceStore.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. /*
  2. Copyright (C) 2017 Cloudbase Solutions SRL
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. // @flow
  15. import { observable, action } from 'mobx'
  16. import { wizardConfig } from '../config'
  17. import type { Instance } from '../types/Instance'
  18. import InstanceSource from '../sources/InstanceSource'
  19. class InstanceStoreUtils {
  20. static hasNextPage(instances) {
  21. let result = false
  22. if (instances.length - 1 === wizardConfig.instancesItemsPerPage) {
  23. result = true
  24. instances.pop()
  25. }
  26. return result
  27. }
  28. static loadFromCache(cache, page) {
  29. let startIndex = wizardConfig.instancesItemsPerPage * (page - 1)
  30. let endIndex = startIndex + wizardConfig.instancesItemsPerPage
  31. return cache.filter((item, index) => {
  32. if (index >= startIndex && index < endIndex) {
  33. return true
  34. }
  35. return false
  36. })
  37. }
  38. }
  39. class InstanceStore {
  40. @observable instances: Instance[] = []
  41. @observable instancesLoading = false
  42. @observable searching = false
  43. @observable searchNotFound: boolean = false
  44. @observable loadingPage = false
  45. @observable currentPage = 1
  46. @observable hasNextPage = false
  47. @observable cachedHasNextPage = false
  48. @observable cachedInstances: Instance[] = []
  49. @observable reloading = false
  50. @observable instancesDetails: Instance[] = []
  51. @observable loadingInstancesDetails = true
  52. @observable instancesDetailsCount: number = 0
  53. @observable instancesDetailsRemaining: number = 0
  54. lastEndpointId: string
  55. reqId: number
  56. @action loadInstances(endpointId: string, skipLimit?: boolean, useCache?: boolean): Promise<void> {
  57. if (this.cachedInstances.length > 0 && this.lastEndpointId === endpointId && useCache) {
  58. return Promise.resolve()
  59. }
  60. this.instancesLoading = true
  61. this.searchNotFound = false
  62. this.lastEndpointId = endpointId
  63. return InstanceSource.loadInstances(endpointId, null, null, skipLimit).then(instances => {
  64. if (endpointId !== this.lastEndpointId) {
  65. return
  66. }
  67. this.currentPage = 1
  68. this.hasNextPage = InstanceStoreUtils.hasNextPage(instances)
  69. this.instances = instances
  70. this.cachedInstances = instances
  71. this.instancesLoading = false
  72. }).catch(() => {
  73. if (endpointId !== this.lastEndpointId) {
  74. return
  75. }
  76. this.instancesLoading = false
  77. })
  78. }
  79. @action searchInstances(endpointId: string, searchText: string) {
  80. this.searching = true
  81. return InstanceSource.loadInstances(endpointId, searchText).then(instances => {
  82. this.currentPage = 1
  83. this.hasNextPage = InstanceStoreUtils.hasNextPage(instances)
  84. this.instances = instances
  85. this.cachedInstances = instances
  86. this.searching = false
  87. this.searchNotFound = Boolean(instances.length === 0 && searchText)
  88. }).catch(() => {
  89. this.searching = false
  90. this.searchNotFound = true
  91. })
  92. }
  93. @action loadNextPage(endpointId: string, searchText: string): Promise<void> {
  94. if (this.cachedInstances.length > wizardConfig.instancesItemsPerPage * this.currentPage) {
  95. this.currentPage = this.currentPage + 1
  96. let numCachedPages = Math.ceil(this.cachedInstances.length / wizardConfig.instancesItemsPerPage)
  97. if (this.currentPage === numCachedPages) {
  98. this.hasNextPage = this.cachedHasNextPage
  99. } else {
  100. this.hasNextPage = true
  101. }
  102. this.instances = InstanceStoreUtils.loadFromCache(this.cachedInstances, this.currentPage)
  103. return Promise.resolve()
  104. }
  105. this.loadingPage = true
  106. return InstanceSource.loadInstances(
  107. endpointId,
  108. searchText,
  109. this.instances[this.instances.length - 1].id
  110. ).then(instances => {
  111. this.hasNextPage = InstanceStoreUtils.hasNextPage(instances)
  112. this.cachedHasNextPage = this.hasNextPage
  113. this.cachedInstances = [...this.cachedInstances, ...instances]
  114. this.instances = instances
  115. this.loadingPage = false
  116. this.currentPage = this.currentPage + 1
  117. }).catch(() => {
  118. this.loadingPage = false
  119. })
  120. }
  121. @action loadPreviousPage() {
  122. this.hasNextPage = true
  123. this.currentPage = this.currentPage - 1
  124. this.instances = InstanceStoreUtils.loadFromCache(this.cachedInstances, this.currentPage)
  125. }
  126. @action reloadInstances(endpointId: string, searchText: string) {
  127. this.reloading = true
  128. this.searchNotFound = false
  129. InstanceSource.loadInstances(endpointId, searchText).then(instances => {
  130. this.reloading = false
  131. this.currentPage = 1
  132. this.hasNextPage = InstanceStoreUtils.hasNextPage(instances)
  133. this.instances = instances
  134. this.cachedInstances = instances
  135. this.searching = false
  136. this.searchNotFound = Boolean(instances.length === 0 && searchText)
  137. }).catch(() => {
  138. this.reloading = false
  139. this.searchNotFound = true
  140. })
  141. }
  142. @action loadInstancesDetails(endpointId: string, instancesInfo: Instance[]): Promise<void> {
  143. // Use reqId to be able to uniquely identify the request so all but the latest request can be igonred and canceled
  144. this.reqId = !this.reqId ? 1 : this.reqId + 1
  145. InstanceSource.cancelInstancesDetailsRequests(this.reqId - 1)
  146. instancesInfo.sort((a, b) => a.instance_name.localeCompare(b.instance_name))
  147. let hash = i => `${i.instance_name}-${i.id || endpointId}`
  148. if (this.instancesDetails.map(hash).join('_') === instancesInfo.map(hash).join('_')) {
  149. return Promise.resolve()
  150. }
  151. let count = instancesInfo.length
  152. this.loadingInstancesDetails = true
  153. this.instancesDetails = []
  154. this.loadingInstancesDetails = true
  155. this.instancesDetailsCount = count
  156. this.instancesDetailsRemaining = count
  157. this.instancesDetails = []
  158. return new Promise((resolve) => {
  159. instancesInfo.forEach(instanceInfo => {
  160. InstanceSource.loadInstanceDetails(endpointId, instanceInfo.instance_name, this.reqId).then((resp: { instance: Instance, reqId: number }) => {
  161. if (resp.reqId !== this.reqId) {
  162. return
  163. }
  164. this.instancesDetailsRemaining -= 1
  165. this.loadingInstancesDetails = this.instancesDetailsRemaining > 0
  166. if (this.instancesDetails.find(i => i.id === resp.instance.id)) {
  167. this.instancesDetails = this.instancesDetails.filter(i => i.id !== resp.instance.id)
  168. }
  169. this.instancesDetails = [
  170. ...this.instancesDetails,
  171. resp.instance,
  172. ]
  173. this.instancesDetails.sort((a, b) => a.instance_name.localeCompare(b.instance_name))
  174. if (this.instancesDetailsRemaining === 0) {
  175. resolve()
  176. }
  177. }).catch((resp?: { reqId: number }) => {
  178. this.instancesDetailsRemaining -= 1
  179. this.loadingInstancesDetails = this.instancesDetailsRemaining > 0
  180. if (!resp || resp.reqId !== this.reqId) {
  181. return
  182. }
  183. if (count === 0) {
  184. resolve()
  185. }
  186. })
  187. })
  188. })
  189. }
  190. @action clearInstancesDetails() {
  191. this.instancesDetails = []
  192. this.loadingInstancesDetails = false
  193. this.instancesDetailsCount = 0
  194. this.instancesDetailsRemaining = 0
  195. }
  196. }
  197. export default new InstanceStore()