| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- /*
- Copyright (C) 2017 Cloudbase Solutions SRL
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- // @flow
- import { observable, runInAction, computed, action } from 'mobx'
- import type { Instance } from '../types/Instance'
- import type { Endpoint } from '../types/Endpoint'
- import InstanceSource from '../sources/InstanceSource'
- import ApiCaller from '../utils/ApiCaller'
- import configLoader from '../utils/Config'
- class InstanceStore {
- @observable instancesLoading = false
- @observable instancesPerPage = 6
- @observable currentPage = 1
- @observable searchChunksLoading = false
- @observable searchedInstances: Instance[] = []
- @observable backgroundInstances: Instance[] = []
- @observable backgroundChunksLoading = false
- @observable searching = false
- @observable searchNotFound = false
- @observable reloading = false
- @observable instancesDetails: Instance[] = []
- @observable loadingInstancesDetails = true
- @observable instancesDetailsCount = 0
- @observable instancesDetailsRemaining = 0
- @observable searchText = ''
- @computed get instances(): Instance[] {
- if (this.searchText && this.searchedInstances.length > 0) {
- return this.searchedInstances
- }
- return this.backgroundInstances
- }
- @computed get chunksLoading(): boolean {
- if (this.searchText) {
- return this.searchChunksLoading
- }
- return this.backgroundChunksLoading
- }
- lastEndpointId: string
- reqId: number
- @action async loadInstancesInChunks(options: {
- endpoint: Endpoint,
- vmsPerPage?: number,
- reload?: boolean,
- env?: any,
- useCache?: boolean,
- }) {
- let { endpoint, vmsPerPage, reload, env, useCache } = options
- vmsPerPage = vmsPerPage || 6
- ApiCaller.cancelRequests(`${endpoint.id}-chunk`)
- this.backgroundInstances = []
- if (reload) {
- this.reloading = true
- } else {
- this.instancesLoading = true
- }
- this.backgroundChunksLoading = true
- this.lastEndpointId = endpoint.id
- let chunkSize = configLoader.config.instancesListBackgroundLoading
- let chunkCount = Math.max(chunkSize[endpoint.type] || chunkSize.default, vmsPerPage)
- let loadNextChunk = async (lastEndpointId?: string) => {
- let currentEndpointId = endpoint.id
- let instances = await InstanceSource.loadInstancesChunk(currentEndpointId, chunkCount, lastEndpointId, `${endpoint.id}-chunk`, undefined, env, useCache)
- if (currentEndpointId !== this.lastEndpointId) {
- return
- }
- let shouldContinue = this.loadInstancesInChunksSuccess(instances, chunkCount, reload)
- if (shouldContinue) {
- loadNextChunk(instances[instances.length - 1].id)
- }
- }
- loadNextChunk()
- }
- @action loadInstancesInChunksSuccess(instances: Instance[], chunkCount: number, reload?: boolean): boolean {
- this.backgroundInstances = [...this.backgroundInstances, ...instances]
- if (reload) {
- this.reloading = false
- }
- this.instancesLoading = false
- if (instances.length < chunkCount) {
- this.backgroundChunksLoading = false
- return false
- }
- return true
- }
- @action async loadInstances(endpointId: string): Promise<void> {
- this.instancesLoading = true
- this.lastEndpointId = endpointId
- try {
- let instances = await InstanceSource.loadInstances(endpointId, true)
- if (endpointId !== this.lastEndpointId) {
- return
- }
- this.loadInstancesSuccess(instances)
- } catch (ex) {
- if (endpointId !== this.lastEndpointId) {
- return
- }
- runInAction(() => { this.instancesLoading = false })
- throw ex
- }
- }
- @action loadInstancesSuccess(instances: Instance[]) {
- this.backgroundInstances = instances
- this.instancesLoading = false
- }
- @action async searchInstances(endpoint: Endpoint, searchText: string) {
- ApiCaller.cancelRequests(`${endpoint.id}-chunk-search`)
- this.searchText = searchText
- this.searchNotFound = false
- if (!searchText) {
- this.currentPage = 1
- this.searchedInstances = []
- return
- }
- if (!this.backgroundChunksLoading) {
- this.searchedInstances = this.backgroundInstances
- .filter(i => (i.instance_name || i.name).toLowerCase().indexOf(searchText.toLowerCase()) > -1)
- this.searchNotFound = Boolean(this.searchedInstances.length === 0)
- this.currentPage = 1
- return
- }
- this.searching = true
- this.searchChunksLoading = true
- let chunkSize = configLoader.config.instancesListBackgroundLoading
- let chunkCount = Math.max(chunkSize[endpoint.type] || chunkSize.default, this.instancesPerPage)
- let loadNextChunk = async (lastEndpointId?: string) => {
- let instances = await InstanceSource.loadInstancesChunk(
- endpoint.id,
- chunkCount,
- lastEndpointId,
- `${endpoint.id}-chunk-search`,
- searchText
- )
- if (this.searching) {
- runInAction(() => {
- this.currentPage = 1
- this.searchedInstances = []
- })
- }
- let shouldContinue = this.searchInstancesSuccess(instances, chunkCount)
- if (shouldContinue) {
- loadNextChunk(instances[instances.length - 1].id)
- }
- }
- loadNextChunk()
- }
- @action searchInstancesSuccess(instances: Instance[], chunkCount: number): boolean {
- this.searchedInstances = [...this.searchedInstances, ...instances]
- this.searching = false
- this.searchNotFound = Boolean(this.searchedInstances.length === 0)
- if (instances.length < chunkCount) {
- this.searchChunksLoading = false
- return false
- }
- return true
- }
- @action reloadInstances(endpoint: Endpoint, chunkSize?: number, env?: any) {
- this.searchNotFound = false
- this.searchText = ''
- this.currentPage = 1
- this.loadInstancesInChunks({ endpoint, vmsPerPage: chunkSize, reload: true, env })
- }
- @action cancelIntancesChunksLoading() {
- ApiCaller.cancelRequests(`${this.lastEndpointId}-chunk`)
- this.lastEndpointId = ''
- this.searchNotFound = false
- this.searchText = ''
- this.currentPage = 1
- }
- @action setPage(page: number) {
- this.currentPage = page
- }
- @action updateInstancesPerPage(instancesPerPage: number) {
- this.currentPage = 1
- this.instancesPerPage = instancesPerPage
- }
- @action async loadInstancesDetailsBulk(
- instanceInfos: {
- endpointId: string,
- instanceNames: string[],
- env?: ?any,
- }[]
- ) {
- this.reqId = !this.reqId ? 1 : this.reqId + 1
- this.instancesDetails = []
- this.loadingInstancesDetails = true
- InstanceSource.cancelInstancesDetailsRequests(this.reqId - 1)
- try {
- await Promise.all(instanceInfos.map(async i => {
- await Promise.all(i.instanceNames.map(async name => {
- let instanceDetails = await InstanceSource.loadInstanceDetails({
- endpointId: i.endpointId,
- instanceName: name,
- reqId: this.reqId,
- quietError: false,
- env: i.env,
- cache: true,
- })
- runInAction(() => {
- this.instancesDetails = this.instancesDetails.filter(i => (i.name || i.instance_name || '') !== name)
- this.instancesDetails.push(instanceDetails.instance)
- this.instancesDetails.sort(n => (n.name || n.instance_name || '')
- .localeCompare(n.name || n.instance_name || ''))
- })
- }))
- }))
- } finally {
- this.loadingInstancesDetails = false
- }
- }
- @action async loadInstancesDetails(opts: {
- endpointId: string,
- instancesInfo: Instance[],
- cache?: boolean,
- quietError?: boolean,
- env?: any,
- targetProvider: string,
- }): Promise<void> {
- let { endpointId, instancesInfo, cache, quietError, env, targetProvider } = opts
- // Use reqId to be able to uniquely identify the request so all but the latest request can be igonred and canceled
- this.reqId = !this.reqId ? 1 : this.reqId + 1
- InstanceSource.cancelInstancesDetailsRequests(this.reqId - 1)
- instancesInfo.sort((a, b) => (a.instance_name || a.name).localeCompare(b.instance_name || b.name))
- let count = instancesInfo.length
- this.loadingInstancesDetails = true
- this.instancesDetails = []
- this.loadingInstancesDetails = true
- this.instancesDetailsCount = count
- this.instancesDetailsRemaining = count
- await new Promise(resolve => {
- Promise.all(instancesInfo.map(async instanceInfo => {
- try {
- let resp: { instance: Instance, reqId: number } =
- await InstanceSource.loadInstanceDetails({
- endpointId,
- instanceName: instanceInfo.instance_name || instanceInfo.name,
- targetProvider,
- reqId: this.reqId,
- quietError,
- env,
- cache,
- })
- if (resp.reqId !== this.reqId) {
- return
- }
- runInAction(() => {
- this.instancesDetailsRemaining -= 1
- this.loadingInstancesDetails = this.instancesDetailsRemaining > 0
- if (this.instancesDetails.find(i => i.id === resp.instance.id)) {
- this.instancesDetails = this.instancesDetails.filter(i => i.id !== resp.instance.id)
- }
- })
- runInAction(() => {
- this.instancesDetails = [
- ...this.instancesDetails,
- resp.instance,
- ]
- this.instancesDetails.sort((a, b) => (a.instance_name || a.name).localeCompare((b.instance_name || b.name)))
- })
- if (this.instancesDetailsRemaining === 0) {
- resolve()
- }
- } catch (err) {
- runInAction(() => {
- this.instancesDetailsRemaining -= 1
- this.loadingInstancesDetails = this.instancesDetailsRemaining > 0
- })
- if (!err || err.reqId !== this.reqId) {
- return
- }
- if (count === 0) {
- resolve()
- }
- }
- }))
- })
- }
- @action clearInstancesDetails() {
- this.instancesDetails = []
- this.loadingInstancesDetails = false
- this.instancesDetailsCount = 0
- this.instancesDetailsRemaining = 0
- }
- }
- export default new InstanceStore()
|