// Copyright 2021 99cloud // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import { get } from 'lodash'; import { action, observable } from 'mobx'; import client from 'client'; import List from 'stores/base-list'; import globalProjectMapStore from 'stores/project'; import globalRootStore from 'stores/root'; export default class BaseStore { list = new List(); @observable detail = {}; @observable isLoading = true; @observable isSubmitting = false; get client() { return {}; } get skylineClient() { return client.skyline; } get responseKey() { return this.client.responseKey; } get listResponseKey() { return `${this.responseKey}s`; } get needGetProject() { return true; } get currentUser() { return globalRootStore.user || {}; } get currentProjectId() { return globalRootStore.projectId; } get hasAdminRole() { return globalRootStore.hasAdminRole; } get enableBilling() { return globalRootStore.enableBilling; } get mapper() { // update response items; return (data) => data; // return ObjectMapper[this.module] || (data => data); } get mapperBeforeFetchProject() { return (data) => data; } get filterByApi() { return false; } get paramsFunc() { if (this.filterByApi) { return (params) => params; } return (params) => { const reservedKeys = [ 'all_data', 'all_projects', 'device_id', 'network_id', 'floating_network_id', 'start_at_gt', 'start_at_lt', 'binary', 'fixed_ip_address', 'device_owner', 'project_id', 'type', 'sort', 'security_group_id', 'id', 'security_group_id', 'owner_id', 'status', 'fingerprint', 'resource_types', 'floating_ip_address', 'uuid', 'loadbalancer_id', 'ikepolicy_id', 'ipsecpolicy_id', 'endpoint_id', 'peer_ep_group_id', 'local_ep_group_id', 'vpnservice_id', ]; const newParams = {}; Object.keys(params).forEach((key) => { if (reservedKeys.indexOf(key) >= 0) { newParams[key] = params[key]; } }); return newParams; }; } get paramsFuncPage() { return (params) => { const { current, ...rest } = params; return rest; }; } get listFilterByProject() { // use it for nuetron apois return false; } get fetchListByLimit() { return false; } get markerKey() { return 'id'; } get listWithDetail() { return false; } get isSubResource() { return false; } getFatherResourceId = (params) => params.id; detailFetchByClient(resourceParams, params) { const { id } = resourceParams; if (!this.isSubResource) { return this.client.show(id, params); } const fatherId = this.getFatherResourceId(resourceParams); return this.client.show(fatherId, id, params); } listFetchByClient(params, originParams) { if (!this.isSubResource) { return this.listWithDetail ? this.client.listDetail(params) : this.client.list(params); } const fatherId = this.getFatherResourceId(originParams); return this.client.list(fatherId, params); } getItemProjectId(item) { return ( item.project_id || item.tenant_id || item.owner || item.owner_id || item.tenant || item.fingerprint ); } itemInCurrentProject = (item, all_projects) => { // use for neutron resource in admin project if (all_projects) { return true; } const itemProject = globalProjectMapStore.getItemProjectId(item); const { shared, visibility, is_public } = item; return ( itemProject === this.currentProjectId || is_public || shared || visibility === 'public' ); }; @action setModule(module) { this.module = module; } @action submitting = (promise) => { this.isSubmitting = true; setTimeout(() => { promise .catch(() => {}) .finally(() => { this.isSubmitting = false; }); }, 500); return promise; }; // eslint-disable-next-line no-unused-vars async listDidFetch(items, allProjects, filters) { return items; } // eslint-disable-next-line no-unused-vars async detailDidFetch(item, all_projects, params) { return item; } async listDidFetchProject(items, all_projects) { if (!this.needGetProject) { return items; } if (!all_projects || !this.hasAdminRole) { return items; } const projectIds = []; items.forEach((item) => { const projectId = globalProjectMapStore.getItemProjectId(item); const projectName = globalProjectMapStore.getItemProjectName(item); if (!projectName && projectId && projectIds.indexOf(projectId) < 0) { projectIds.push(projectId); } }); if (projectIds.length === 0) { return items; } try { const results = await Promise.all( projectIds.map((id) => globalProjectMapStore.fetchProjectDetail({ id })) ); items.forEach((item) => { const projectId = globalProjectMapStore.getItemProjectId(item); if (projectId && projectIds.indexOf(projectId) >= 0) { const project = results.find((it) => it.id === projectId); item.project_name = project ? project.name || '-' : '-'; } }); } catch (e) { return items; } return items; } updateMarkerParams = (limit, marker) => { return { limit, marker, }; }; async requestListByMarker(params, limit, marker) { const markerParams = this.updateMarkerParams(limit, marker); const newParams = { ...params, ...markerParams, }; return this.listFetchByClient(newParams); } async requestListAllByLimit(params, limit) { let marker = ''; let hasNext = true; let datas = []; while (hasNext) { // eslint-disable-next-line no-await-in-loop const result = await this.requestListByMarker(params, limit, marker); const data = this.getListDataFromResult(result); datas = [...datas, ...data]; if (data.length >= limit) { marker = this.parseMarker(data, result, datas); if (!marker) { // eslint-disable-next-line no-console console.log('parse marker error!'); hasNext = false; } } else { hasNext = false; } } return datas; } async requestListAll(params, originParams) { const result = await this.listFetchByClient(params, originParams); return this.getListDataFromResult(result); } async requestList(params, originParams) { const datas = !this.fetchListByLimit ? await this.requestListAll(params, originParams) : await this.requestListAllByLimit(params, 100); return datas; } // eslint-disable-next-line no-unused-vars async requestListByPage(params, page, originParams) { const datas = await this.listFetchByClient(params, originParams); return datas; } // eslint-disable-next-line no-unused-vars updateUrl = (url, params) => url; // eslint-disable-next-line no-unused-vars updateParamsSortPage = (params, sortKey, sortOrder) => {}; // eslint-disable-next-line no-unused-vars updateParamsSort = (params, sortKey, sortOrder) => {}; @action async pureFetchList({ limit, page, sortKey, sortOrder, conditions, timeFilter, ...filters } = {}) { // todo: no page, no limit, fetch all const { tab, all_projects, ...rest } = filters; const params = { ...rest }; if (all_projects) { if (!this.listFilterByProject) { params.all_projects = true; } } const allData = await this.requestList(params, {}); return allData; } @action async fetchList({ limit, page, sortKey, sortOrder, conditions, timeFilter, ...filters } = {}) { this.list.isLoading = true; // todo: no page, no limit, fetch all const { tab, all_projects, ...rest } = filters; const params = { ...rest }; if (all_projects) { if (!this.listFilterByProject) { params.all_projects = true; } } const newParams = this.paramsFunc(params); const allData = await this.requestList(newParams, filters); const allDataNew = allData.map((it) => this.mapperBeforeFetchProject(it, filters) ); const data = allDataNew.filter((item) => { if (!this.listFilterByProject) { return true; } return this.itemInCurrentProject(item, all_projects); }); // const items = data.map(this.mapper); let newData = await this.listDidFetchProject(data, all_projects); try { newData = await this.listDidFetch(newData, all_projects, filters); } catch (e) { // eslint-disable-next-line no-console console.log(e); } newData = newData.map(this.mapper); this.list.update({ data: newData, total: newData.length || 0, limit: Number(limit) || 10, page: Number(page) || 1, sortKey, sortOrder, filters, timeFilter, isLoading: false, ...(this.list.silent ? {} : { selectedRowKeys: [] }), }); return newData; } // eslint-disable-next-line no-unused-vars parseMarker(datas, result, allDatas) { return datas.length === 0 ? '' : get(datas[datas.length - 1], this.markerKey); } @action updateMarker(datas, page, result, allDatas) { const marker = this.parseMarker(datas, result, allDatas); if (page === 1) { this.list.markers = [marker]; } else { this.list.markers[page - 1] = marker; } } getMarker(page) { return page === 1 ? '' : this.list.markers[page - 2]; } getDetailParams = () => undefined; getListDataFromResult = (result) => this.listResponseKey ? get(result, this.listResponseKey, []) : result; // eslint-disable-next-line no-unused-vars async getCountForPage(newParams, all_projects, newDatas) { return {}; } // eslint-disable-next-line no-unused-vars getOtherInfo = (result) => {}; @action async fetchListByPage({ limit = 10, page = 1, sortKey, sortOrder, conditions, timeFilter, ...filters } = {}) { this.list.isLoading = true; // todo: no page, no limit, fetch all const { tab, all_projects, ...rest } = filters; const params = { limit, ...rest }; this.updateParamsSortPage(params, sortKey, sortOrder); if (all_projects) { if (!this.listFilterByProject) { params.all_projects = true; } } const marker = this.getMarker(page); if (marker) { params.marker = marker; } const newParams = this.paramsFuncPage(params, all_projects); const result = await this.requestListByPage(newParams, page, filters); const allData = this.getListDataFromResult(result); this.updateMarker(allData, page, result, allData); const allDataNew = allData.map(this.mapperBeforeFetchProject); let newData = await this.listDidFetchProject(allDataNew, all_projects); newData = await this.listDidFetch(newData, all_projects, filters); newData = newData.map(this.mapper); let count; let total; if (result.count || result.total) { count = result.count || result.total; } else { const totalResult = await this.getCountForPage( newParams, newData, all_projects, result, params ); const { count: retCount, total: retTotal } = totalResult; count = retCount; total = retTotal; } const others = this.getOtherInfo(result); this.list.update({ data: newData, limit: Number(limit) || 10, page: Number(page) || 1, sortKey, sortOrder, filters, timeFilter, isLoading: false, total: count || total, ...(this.list.silent ? {} : { selectedRowKeys: [] }), ...others, }); return newData; } @action async fetchDetail({ all_projects, silent, ...rest }) { if (!silent) { this.isLoading = true; } const result = await this.detailFetchByClient( rest, this.getDetailParams({ all_projects }) ); const originData = get(result, this.responseKey) || result; const item = this.mapperBeforeFetchProject(originData, rest, true); try { const newItem = await this.detailDidFetch(item, all_projects, rest); const detail = this.mapper(newItem); this.detail = detail; } catch (e) { // eslint-disable-next-line no-console console.log(e); this.detail = item; } this.isLoading = false; return this.detail; } @action setSelectRowKeys(key, selectedRowKeys) { this[key] && this[key].selectedRowKeys.replace(selectedRowKeys); } @action create(data) { const body = {}; body[this.responseKey] = data; return this.submitting(this.client.create(body)); } @action edit({ id }, newObject) { const body = {}; body[this.responseKey] = newObject; return this.submitting(this.client.update(id, body)); } @action update({ id }, newObject) { const body = {}; body[this.responseKey] = newObject; return this.submitting(this.client.update(id, body)); } @action patch({ id }, newObject) { return this.submitting(this.client.patch(id, newObject)); } @action delete = ({ id }) => this.submitting(this.client.delete(id)); @action batchDelete(rowKeys) { return this.submitting( Promise.all( rowKeys.map((name) => { const item = this.list.data.find((_item) => _item.name === name); const { id } = item; return this.client.delete(id); }) ) ); } @action clearData() { this.list.reset(); this.detail = {}; } }