diff --git a/config/webpack.common.js b/config/webpack.common.js index ce1e68ab..82b26ccc 100644 --- a/config/webpack.common.js +++ b/config/webpack.common.js @@ -127,6 +127,7 @@ module.exports = { locales: root('src/locales'), styles: root('src/styles'), resources: root('src/resources'), + client: root('src/client'), }, }, plugins: [ diff --git a/package.json b/package.json index 7dda56c7..97cd403c 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,8 @@ "react-highcharts": "^16.0.2", "react-router": "^4.3.1", "react-router-dom": "^4.3.1", - "react-sortable-hoc": "1.11.0" + "react-sortable-hoc": "1.11.0", + "uuid": "^8.3.2" }, "devDependencies": { "@babel/core": "^7.14.3", diff --git a/src/api/cinder/base.js b/src/api/cinder/base.js index a232d23b..441ba487 100644 --- a/src/api/cinder/base.js +++ b/src/api/cinder/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { cinderBase } from 'utils/constants'; +import { cinderBase } from 'client/client/constants'; const getCinderBaseUrl = (key) => `${cinderBase()}/${key}`; diff --git a/src/api/glance/base.js b/src/api/glance/base.js index 485ed93a..06de3c80 100644 --- a/src/api/glance/base.js +++ b/src/api/glance/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { glanceBase } from 'utils/constants'; +import { glanceBase } from 'client/client/constants'; const getGlanceBaseUrl = (key) => `${glanceBase()}/${key}`; diff --git a/src/api/gocron/base.js b/src/api/gocron/base.js index 47e29752..fbee73db 100644 --- a/src/api/gocron/base.js +++ b/src/api/gocron/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { gocronBase } from 'utils/constants'; +import { gocronBase } from 'client/client/constants'; const getGocronBaseUrl = (key) => `${gocronBase()}/${key}`; diff --git a/src/api/heat/base.js b/src/api/heat/base.js index aee33519..a6fcd3c4 100644 --- a/src/api/heat/base.js +++ b/src/api/heat/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { heatBase } from 'utils/constants'; +import { heatBase } from 'client/client/constants'; const getHeatBaseUrl = (key) => `${heatBase()}/${key}`; diff --git a/src/api/ironic-inspector/base.js b/src/api/ironic-inspector/base.js index 61d557ba..296d6328 100644 --- a/src/api/ironic-inspector/base.js +++ b/src/api/ironic-inspector/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { ironicInspectorBase } from 'utils/constants'; +import { ironicInspectorBase } from 'client/client/constants'; const getIronicInspectorBaseUrl = (key) => `${ironicInspectorBase()}/${key}`; diff --git a/src/api/ironic/base.js b/src/api/ironic/base.js index bc909b08..efe92619 100644 --- a/src/api/ironic/base.js +++ b/src/api/ironic/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { ironicBase } from 'utils/constants'; +import { ironicBase } from 'client/client/constants'; const getIronicBaseUrl = (key) => `${ironicBase()}/${key}`; diff --git a/src/api/keystone/base.js b/src/api/keystone/base.js index cb7cad0e..df7a5a01 100644 --- a/src/api/keystone/base.js +++ b/src/api/keystone/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { keystoneBase } from 'utils/constants'; +import { keystoneBase } from 'client/client/constants'; const getKeystoneBaseUrl = (key) => `${keystoneBase()}/${key}`; diff --git a/src/api/neutron/base.js b/src/api/neutron/base.js index d30feb56..dacb44c7 100644 --- a/src/api/neutron/base.js +++ b/src/api/neutron/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { neutronBase } from 'utils/constants'; +import { neutronBase } from 'client/client/constants'; const getNeutronBaseUrl = (key) => `${neutronBase()}/${key}`; diff --git a/src/api/nova/base.js b/src/api/nova/base.js index 0d4a3366..0f29be21 100644 --- a/src/api/nova/base.js +++ b/src/api/nova/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { novaBase } from 'utils/constants'; +import { novaBase } from 'client/client/constants'; const getNovaBaseUrl = (key) => `${novaBase()}/${key}`; diff --git a/src/api/octavia/base.js b/src/api/octavia/base.js index 85f0e052..942537f0 100644 --- a/src/api/octavia/base.js +++ b/src/api/octavia/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { octaviaBase } from 'utils/constants'; +import { octaviaBase } from 'client/client/constants'; const getOctaviaBaseUrl = (key) => `${octaviaBase()}/${key}`; diff --git a/src/api/panko/base.js b/src/api/panko/base.js index e3b159d3..3e2ab460 100644 --- a/src/api/panko/base.js +++ b/src/api/panko/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { pankoBase } from 'utils/constants'; +import { pankoBase } from 'client/client/constants'; const getPankoBaseUrl = (key) => `${pankoBase()}/${key}`; diff --git a/src/api/placement/base.js b/src/api/placement/base.js index 30017b79..1d5d9f55 100644 --- a/src/api/placement/base.js +++ b/src/api/placement/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { placementBase } from 'utils/constants'; +import { placementBase } from 'client/client/constants'; const getPlacementBaseUrl = (key) => `${placementBase()}/${key}`; diff --git a/src/api/skyline/base.js b/src/api/skyline/base.js index 19765c92..3c52be07 100644 --- a/src/api/skyline/base.js +++ b/src/api/skyline/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { skylineBase } from 'utils/constants'; +import { skylineBase } from 'client/client/constants'; const getSkylineBaseUrl = (key) => `${skylineBase()}/${key}`; diff --git a/src/api/swift/base.js b/src/api/swift/base.js index 1b78587a..b7a0ee4e 100644 --- a/src/api/swift/base.js +++ b/src/api/swift/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { swiftBase } from 'utils/constants'; +import { swiftBase } from 'client/client/constants'; const getSwiftBaseUrl = (key) => `${swiftBase()}/${key}`; diff --git a/src/client/cinder/index.js b/src/client/cinder/index.js new file mode 100644 index 00000000..578b81cf --- /dev/null +++ b/src/client/cinder/index.js @@ -0,0 +1,155 @@ +// 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 Base from '../client/base'; +import { cinderBase } from '../client/constants'; + +class CinderClient extends Base { + get baseUrl() { + return cinderBase(); + } + + get projectInUrl() { + return true; + } + + get resources() { + return [ + { + key: 'volumes', + responseKey: 'volume', + extendOperations: [ + { + key: 'action', + method: 'post', + }, + ], + }, + { + key: 'types', + responseKey: 'volume_type', + extendOperations: [ + { + key: 'action', + method: 'post', + }, + { + name: 'getAccess', + key: 'os-volume-type-access', + }, + ], + subResources: [ + { + name: 'extraSpecs', + key: 'extra_specs', + responseKey: 'extra_spec', + }, + { + key: 'encryption', + }, + ], + }, + { + key: 'snapshots', + responseKey: 'snapshot', + }, + { + key: 'backups', + responseKey: 'backup', + extendOperations: [ + { + key: 'restore', + isDetail: true, + method: 'post', + }, + ], + }, + { + name: 'backupChains', + key: 'backup_chains', + responseKey: 'backup_chain', + extendOperations: [ + { + key: 'restore', + isDetail: true, + method: 'post', + }, + ], + }, + { + name: 'pools', + key: 'scheduler-stats/get_pools', + responseKey: 'pool', + }, + { + name: 'qosSpecs', + key: 'qos-specs', + responseKey: 'qos_spec', + extendOperations: [ + { + name: 'deleteKeys', + key: 'delete_keys', + method: 'put', + }, + { + key: 'associate', + }, + { + key: 'disassociate', + }, + ], + }, + { + name: 'services', + key: 'os-services', + responseKey: 'service', + extendOperations: [ + { + key: 'enable', + isDetail: false, + method: 'put', + }, + { + name: 'reason', + key: 'disable-log-reason', + isDetail: false, + method: 'put', + }, + ], + }, + { + name: 'quotaSets', + key: 'os-quota-sets', + responseKey: 'quota_set', + }, + { + name: 'azones', + key: 'os-availability-zone', + }, + { + name: 'volumeTransfers', + key: 'volume-transfers', + extendOperations: [ + { + key: 'accept', + method: 'post', + }, + ], + }, + ]; + } +} + +const cinderClient = new CinderClient(); +export default cinderClient; diff --git a/src/client/client/base.js b/src/client/client/base.js new file mode 100644 index 00000000..b6e0e3f6 --- /dev/null +++ b/src/client/client/base.js @@ -0,0 +1,420 @@ +// 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 globalRootStore from 'stores/root'; +import clientRequest from './request'; + +export default class BaseClient { + constructor() { + this.generateAll(); + } + + getUrl = (url) => { + if (this.projectInUrl) { + return `${this.baseUrl}/${this.project}/${url}`; + } + return `${this.baseUrl}/${url}`; + }; + + get request() { + const { request } = clientRequest; + return { + get: (url, params, conf) => request.get(this.getUrl(url), params, conf), + post: (url, data, params, conf) => + request.post(this.getUrl(url), data, params, conf), + put: (url, data, params, conf) => + request.put(this.getUrl(url), data, params, conf), + delete: (url, data, params, conf) => + request.delete(this.getUrl(url), data, params, conf), + patch: (url, data, params, conf) => + request.patch(this.getUrl(url), data, params, conf), + head: (url, params, conf) => request.head(this.getUrl(url), params, conf), + }; + } + + get originRequest() { + const { request } = clientRequest; + return request; + } + + get params() { + return []; + } + + get baseUrl() { + return ''; + } + + get projectInUrl() { + return false; + } + + get project() { + if (!this.projectInUrl) { + return ''; + } + const { project: { id } = {} } = globalRootStore.user || {}; + return id || ''; + } + + get enabled() { + return true; + } + + get resources() { + return []; + } + + getListUrl(resourceName) { + return resourceName; + } + + getDetailUrl(resourceName, id) { + if (!id) { + return resourceName; + } + if (resourceName[resourceName.length - 1] === '/') { + return `${resourceName.substr(0, resourceName.length - 1)}/${id}`; + } + return `${resourceName}/${id}`; + } + + getSubResourceUrl(resourceName, subResourceName) { + if (!resourceName) { + return subResourceName; + } + if (resourceName[resourceName.length - 1] === '/') { + return `${resourceName.substr( + 0, + resourceName.length - 1 + )}/${subResourceName}`; + } + return `${resourceName}/${subResourceName}`; + } + + getSubResourceUrlById(resourceName, subResourceName, id) { + return `${this.getDetailUrl(resourceName, id)}/${subResourceName}`; + } + + getSubResourceUrlBySubId(resourceName, subResourceName, id, subId) { + return `${this.getSubResourceUrlById( + resourceName, + subResourceName, + id + )}/${subId}`; + } + + getSubSubResourceListUrl( + resourceName, + subResourceName, + subSubResourceName, + id, + subId + ) { + return `${this.getSubResourceUrlBySubId( + resourceName, + subResourceName, + id, + subId + )}/${subSubResourceName}`; + } + + getSubSubResourceDetailUrl( + resourceName, + subResourceName, + subSubResourceName, + id, + subId, + subSubId + ) { + return `${this.getSubSubResourceListUrl( + resourceName, + subResourceName, + subSubResourceName, + id, + subId + )}/${subSubId}`; + } + + generateResource = (resourceName, responseKey, enabled = true) => { + const listUrl = this.getListUrl(resourceName); + return { + list: (params, conf) => this.request.get(listUrl, params, conf), + listDetail: (params, conf) => + this.request.get(`${listUrl}/detail`, params, conf), + show: (id, params, conf) => { + return this.request.get( + this.getDetailUrl(resourceName, id), + params, + conf + ); + }, + create: (data, ...args) => this.request.post(listUrl, data, ...args), + update: (id, data, ...args) => + this.request.put(this.getDetailUrl(resourceName, id), data, ...args), + patch: (id, data, ...args) => + this.request.patch(this.getDetailUrl(resourceName, id), data, ...args), + delete: (id, ...args) => + this.request.delete(this.getDetailUrl(resourceName, id), ...args), + responseKey, + enabled, + }; + }; + + generateSubResource = ( + resourceName, + subResourceName, + responseKey, + enabled + ) => ({ + list: (id, params, ...args) => + this.request.get( + this.getSubResourceUrlById(resourceName, subResourceName, id), + params, + ...args + ), + listDetail: (id, params, ...args) => + this.request.get( + `${this.getSubResourceUrlById( + resourceName, + subResourceName, + id + )}/detail`, + params, + ...args + ), + show: (id, subId, params, ...args) => + this.request.get( + this.getSubResourceUrlBySubId(resourceName, subResourceName, id, subId), + params, + ...args + ), + create: (id, data, ...args) => + this.request.post( + this.getSubResourceUrlById(resourceName, subResourceName, id), + data, + ...args + ), + update: (id, subId, data, ...args) => + this.request.put( + this.getSubResourceUrlBySubId(resourceName, subResourceName, id, subId), + data, + ...args + ), + patch: (id, subId, data, ...args) => + this.request.patch( + this.getSubResourceUrlBySubId(resourceName, subResourceName, id, subId), + data, + ...args + ), + delete: (id, subId, ...args) => + this.request.delete( + this.getSubResourceUrlBySubId(resourceName, subResourceName, id, subId), + ...args + ), + responseKey, + enabled, + }); + + generateSubSonResource = ( + resourceName, + subResourceName, + subSubResonseName, + responseKey + ) => ({ + list: (id, subId, params, ...args) => + this.request.get( + this.getSubSubResourceListUrl( + resourceName, + subResourceName, + subSubResonseName, + id, + subId + ), + params, + ...args + ), + show: (id, subId, subSubId, params, ...args) => + this.request.get( + this.getSubSubResourceDetailUrl( + resourceName, + subResourceName, + subSubResonseName, + id, + subId, + subSubId + ), + params, + ...args + ), + create: (id, subId, data, ...args) => + this.request.post( + this.getSubSubResourceListUrl( + resourceName, + subResourceName, + subSubResonseName, + id, + subId + ), + data, + ...args + ), + update: (id, subId, subSubId, data, ...args) => + this.request.put( + this.getSubSubResourceDetailUrl( + resourceName, + subResourceName, + subSubResonseName, + id, + subId, + subSubId + ), + data, + ...args + ), + patch: (id, subId, subSubId, data, ...args) => + this.request.patch( + this.getSubSubResourceDetailUrl( + resourceName, + subResourceName, + subSubResonseName, + id, + subId, + subSubId + ), + data, + ...args + ), + delete: (id, subId, subSubId, ...args) => + this.request.delete( + this.getSubSubResourceDetailUrl( + resourceName, + subResourceName, + subSubResonseName, + id, + subId, + subSubId + ), + ...args + ), + responseKey, + }); + + setRequest = (url, method, ...restArgs) => { + const lowerMethod = method.toLowerCase(); + return this.request[lowerMethod](url, ...restArgs); + }; + + generateAll = () => { + this.resources.forEach((resource) => { + const { + name, + key, + responseKey, + enabled, + subResources = [], + isResource = true, + extendOperations = [], + } = resource; + const result = isResource + ? this.generateResource(key, responseKey, enabled) + : {}; + const realName = name || key; + extendOperations.forEach((other) => { + const { + name: otherName, + key: otherKey, + method = 'get', + isDetail, + generate, + url, + } = other; + const otherRealName = otherName || otherKey; + const otherUrl = url && url(); + const otherIsDetail = isResource + ? isDetail === undefined + ? true + : isDetail + : isDetail === undefined + ? false + : isDetail; + if (generate) { + result[otherRealName] = generate; + } else if (otherIsDetail) { + result[otherRealName] = (id, ...args) => { + return this.setRequest( + otherUrl || this.getSubResourceUrlById(key, otherKey, id), + method, + ...args + ); + }; + } else { + result[otherRealName] = (...args) => { + return this.setRequest( + otherUrl || this.getSubResourceUrl(key, otherKey), + method, + ...args + ); + }; + } + }); + subResources.forEach((sub) => { + let subResult = {}; + const { + name: subName, + key: subKey, + responseKey: subResponseKey, + method: subMethod, + enabled: subEnabled, + subResources: subSubResources = [], + } = sub; + const subRealName = subName || subKey; + if (!subMethod) { + subResult = this.generateSubResource( + key, + subKey, + subResponseKey, + subEnabled + ); + } else { + subResult = (id, ...args) => { + const url = this.getSubResourceUrlById(key, subKey, id); + return this.setRequest(url, subMethod, ...args); + }; + } + subSubResources.forEach((son) => { + const { + key: sonKey, + name: sonName, + responseKey: sonResponseKey, + } = son; + subResult[sonName || sonKey] = this.generateSubSonResource( + key, + subKey, + sonKey, + sonResponseKey + ); + }); + result[subRealName] = subResult; + }); + if (realName) { + this[realName] = result; + } else { + Object.keys(result).forEach((resultKey) => { + this[resultKey] = result[resultKey]; + }); + } + }); + }; +} diff --git a/src/client/client/constants.js b/src/client/client/constants.js new file mode 100644 index 00000000..14392b27 --- /dev/null +++ b/src/client/client/constants.js @@ -0,0 +1,125 @@ +// 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 globalRootStore from 'stores/root'; +import { toJS } from 'mobx'; + +export const groupNameVersionMap = { + core: 'v1', + system: 'v1', +}; + +const endpointVersionMap = { + keystone: 'v3', + nova: 'v2.1', + cinder: 'v3', + glance: 'v2', + neutron: 'v2.0', + ironic: 'v1', + ironicInspector: 'v1', + heat: 'v1', + swift: 'v1', + octavia: 'v2', + courier: 'v1', + prometheus: 'api/v1', + prometheus_sidecar: 'api/v1', + gocron: 'api', + panko: 'v2', + billing_system: 'api/core.io/v1', + workflow: 'api/core.io/v1', +}; + +const endpointsDefault = { + ironic: '/api/openstack/ironic', + ironicInspector: '/api/openstack/ironic-inspector', + swift: '/api/openstack/swift/swift', + octavia: '/api/openstack/octavia', +}; + +export const getOpenstackEndpoint = (key) => { + const { endpoints = {} } = globalRootStore || {}; + const version = endpointVersionMap[key]; + const endpoint = endpoints[key] || endpointsDefault[key] || ''; + return version ? `${endpoint}/${version}` : endpoint; +}; + +export const getOriginEndpoint = (key) => { + const endpoints = toJS((globalRootStore && globalRootStore.endpoints) || {}); + return endpoints[key]; +}; + +export const skylineBase = () => '/api/openstack/skyline/api/v1'; +export const keystoneBase = () => getOpenstackEndpoint('keystone'); +export const novaBase = () => getOpenstackEndpoint('nova'); +export const cinderBase = () => getOpenstackEndpoint('cinder'); +export const glanceBase = () => getOpenstackEndpoint('glance'); +export const neutronBase = () => getOpenstackEndpoint('neutron'); +export const ironicBase = () => getOpenstackEndpoint('ironic'); +export const ironicInspectorBase = () => + getOpenstackEndpoint('ironicInspector'); +export const placementBase = () => getOpenstackEndpoint('placement'); +export const heatBase = () => getOpenstackEndpoint('heat'); +export const swiftBase = () => getOpenstackEndpoint('swift'); +export const octaviaBase = () => getOpenstackEndpoint('octavia'); +export const alertmanagerBase = () => getOpenstackEndpoint('alertmanager'); +export const prometheusBase = () => getOpenstackEndpoint('prometheus'); +export const prometheusSidecarBase = () => + getOpenstackEndpoint('prometheus_sidecar'); +export const courierBase = () => getOpenstackEndpoint('courier'); +export const gocronBase = () => getOpenstackEndpoint('gocron'); +export const pankoBase = () => getOpenstackEndpoint('panko'); +export const s3Base = () => getOpenstackEndpoint('s3'); +export const billingBase = () => getOpenstackEndpoint('billing_system'); +export const workflowBase = () => getOpenstackEndpoint('workflow'); + +export const ironicOriginEndpoint = () => getOriginEndpoint('ironic'); +export const s3OriginEndpoint = () => getOriginEndpoint('s3'); +export const billingEndpoint = () => getOriginEndpoint('billing_system'); +export const firewallEndpoint = () => getOriginEndpoint('neutron_firewall'); +export const vpnEndpoint = () => getOriginEndpoint('neutron_vpn'); +export const lbEndpoint = () => getOriginEndpoint('octavia'); + +export const apiVersionMaps = { + nova: { + key: 'Openstack-Api-Version', + value: 'compute 2.79', + }, + placement: { + key: 'Openstack-Api-Version', + value: 'placement 1.28', + }, + cinder: { + key: 'Openstack-Api-Version', + value: 'volume 3.59', + }, + ironic: { + key: 'X-Openstack-Ironic-Api-Version', + value: '1.58', + }, + 'ironic-inspect': { + key: 'X-OpenStack-Ironic-Inspector-API-Version', + value: '1.15', + }, +}; + +export const getOpenstackApiVersion = (url) => { + const key = Object.keys(apiVersionMaps).find((it) => url.indexOf(it) > -1); + if (!key) { + return null; + } + return apiVersionMaps[key]; +}; + +export const getK8sTypeEndpoint = (groupName, baseUrl) => + `${baseUrl}/${groupName}/${groupNameVersionMap[groupName]}`; diff --git a/src/client/client/request.js b/src/client/client/request.js new file mode 100644 index 00000000..88943b75 --- /dev/null +++ b/src/client/client/request.js @@ -0,0 +1,181 @@ +// 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 Axios from 'axios'; +import { getLocalStorageItem } from 'utils/local-storage'; +import { isEqual } from 'lodash'; +import qs from 'qs'; +import globalRootStore from 'stores/root'; +import { v4 as uuidv4 } from 'uuid'; +import { getOpenstackApiVersion } from './constants'; + +const METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD']; +/** + * @class HttpRequest + * request with axios + */ +export class HttpRequest { + constructor() { + this.request = {}; + } + + /** + * @param instance instance of axios + * @param url request url + * interceptors includes request & response + * @returns {void} + */ + interceptors(instance, url) { + instance.interceptors.request.use( + (config) => { + const uuid = uuidv4(); + config.headers['X-Openstack-Request-Id'] = `req-${uuid}`; + const keystoneToken = getLocalStorageItem('keystone_token') || ''; + const apiVersionMap = getOpenstackApiVersion(url); + if (keystoneToken) { + config.headers['X-Auth-Token'] = keystoneToken; + } + if (apiVersionMap) { + config.headers[apiVersionMap.key] = apiVersionMap.value; + } + const { options: { headers, isFormData, ...rest } = {} } = config; + if (!isEqual(headers)) { + config.headers = { + ...config.headers, + ...headers, + }; + } + if (isFormData) { + delete config.headers['Content-Type']; + } + Object.keys(rest).forEach((key) => { + config[key] = rest[key]; + }); + return config; + }, + (err) => Promise.reject(err) + ); + + instance.interceptors.response.use( + (response) => { + // request is finished + const { data, status } = response; + const disposition = response.headers['content-disposition'] || ''; + const contentType = response.headers['content-type'] || ''; + if (contentType.includes('application/octet-stream')) { + return response; + } + if (disposition.includes('attachment')) { + return response; + } + if (status < 200 || status >= 300) { + return Promise.reject(data); + } + return data; + }, + (error) => { + // request is finished + // eslint-disable-next-line no-console + console.log('error.response', error.response, error); + if (error.response) { + const { status } = error.response; + if (status === 401) { + const currentPath = window.location.pathname; + if (currentPath.indexOf('login') < 0) { + globalRootStore.gotoLoginPage(currentPath); + } + } + } + return Promise.reject(error); + } + ); + } + + /** + * create a new instance of axios with a custom config + */ + create() { + const conf = { + baseURL: '/', + headers: { + 'Content-Type': 'application/json;charset=utf-8', + 'cache-control': 'no-cache', + pragma: 'no-cache', + }, + }; + return Axios.create(conf); + } + + /** + * @param {Object} obj translated object + * @returns {Object} trim undefined & null + */ + omitNil(obj) { + if (typeof obj !== 'object') return obj; + return Object.keys(obj).reduce((acc, v) => { + if (obj[v] !== undefined && obj[v] !== null && obj[v] !== '') + acc[v] = obj[v]; + return acc; + }, {}); + } + + /** + * build request + * @param {Object} config requests config + * @returns {Promise} axios instance return promise + */ + buildRequest(config) { + const method = config.method ? config.method.toLowerCase() : 'get'; + const options = { ...config }; + // Only get and head, we need to use null for some posts requests + if (options.params && ['get', 'head'].includes(method)) { + options.params = this.omitNil(options.params); + options.paramsSerializer = (p) => + qs.stringify(p, { arrayFormat: 'repeat' }); + } + const instance = this.create(); + this.interceptors(instance, options.url); + return instance(options); + } + + generateRequestMap = () => { + METHODS.forEach((method) => { + const lowerMethod = method.toLowerCase(); + if (lowerMethod === 'get' || lowerMethod === 'head') { + this.request[lowerMethod] = (url, params = {}, options) => { + return this.buildRequest({ + method: lowerMethod, + url, + params, + options, + }); + }; + } else { + this.request[lowerMethod] = (url, data, params, options) => { + return this.buildRequest({ + method: lowerMethod, + url, + data, + params, + options, + }); + }; + } + }); + }; +} + +const httpRequest = new HttpRequest(); +httpRequest.generateRequestMap(); +export default httpRequest; diff --git a/src/client/glance/index.js b/src/client/glance/index.js new file mode 100644 index 00000000..8ac50522 --- /dev/null +++ b/src/client/glance/index.js @@ -0,0 +1,87 @@ +// 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 Base from '../client/base'; +import { glanceBase } from '../client/constants'; + +class GlanceClient extends Base { + get baseUrl() { + return glanceBase(); + } + + get resources() { + return [ + { + key: 'images', + responseKey: 'image', + extendOperations: [ + { + key: 'count', + isDetail: false, + }, + { + key: 'uploadFile', + generate: (id, body, conf = {}) => { + return this.request.put( + `${this.getDetailUrl('images', id)}/file`, + body, + null, + { + headers: { + 'content-type': 'application/octet-stream', + }, + ...conf, + } + ); + }, + }, + { + key: 'patch', + generate: (id, data) => + this.request.patch(this.getDetailUrl('images', id), data, null, { + headers: { + 'content-type': + 'application/openstack-images-v2.1-json-patch', + }, + }), + }, + ], + subResources: [ + { + key: 'members', + }, + ], + }, + { + name: 'namespaces', + key: 'metadefs/namespaces', + responseKey: 'namespace', + subResources: [ + { + name: 'resourceTypes', + key: 'resource_types', + }, + ], + }, + { + name: 'resourceTypes', + key: 'metadefs/resource_types', + responseKey: 'resource_type', + }, + ]; + } +} + +const glanceClient = new GlanceClient(); +export default glanceClient; diff --git a/src/client/heat/index.js b/src/client/heat/index.js new file mode 100644 index 00000000..20c1470a --- /dev/null +++ b/src/client/heat/index.js @@ -0,0 +1,93 @@ +// 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 Base from '../client/base'; +import { heatBase } from '../client/constants'; + +class HeatClient extends Base { + get baseUrl() { + return heatBase(); + } + + get projectInUrl() { + return true; + } + + getDetailUrlForStack = ({ id, name }) => `stacks/${name}/${id}`; + + get resources() { + return [ + { + key: 'stacks', + responseKey: 'stack', + extendOperations: [ + { + key: 'show', + generate: ({ id, name }, params) => { + return this.request.get( + this.getDetailUrlForStack({ id, name }), + params + ); + }, + }, + { + key: 'update', + generate: ({ id, name }, data) => + this.request.put(this.getDetailUrlForStack({ id, name }), data), + }, + { + key: 'delete', + generate: ({ id, name }) => + this.request.delete(this.getDetailUrlForStack({ id, name })), + }, + { + key: 'abandon', + generate: ({ id, name }) => + this.request.delete( + `${this.getDetailUrlForStack({ id, name })}/abandon` + ), + }, + { + key: 'template', + generate: ({ id, name }) => + this.request.get( + `${this.getDetailUrlForStack({ id, name })}/template` + ), + }, + { + key: 'events', + generate: ({ id, name }) => + this.request.get( + `${this.getDetailUrlForStack({ id, name })}/events` + ), + }, + { + key: 'resources', + generate: ({ id, name }) => + this.request.get( + `${this.getDetailUrlForStack({ id, name })}/resources` + ), + }, + ], + }, + { + key: 'services', + responseKey: 'service', + }, + ]; + } +} + +const heatClient = new HeatClient(); +export default heatClient; diff --git a/src/client/index.js b/src/client/index.js new file mode 100644 index 00000000..e5e6b59d --- /dev/null +++ b/src/client/index.js @@ -0,0 +1,41 @@ +// 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 skyline from './skyline'; +import nova from './nova'; +import cinder from './cinder'; +import glance from './glance'; +import neutron from './neutron'; +import keystone from './keystone'; +import heat from './heat'; +import octavia from './octavia'; +import placement from './placement'; +import ironic from './ironic'; + +const client = { + skyline, + nova, + cinder, + glance, + neutron, + keystone, + heat, + octavia, + placement, + ironic, +}; + +window.client = client; + +export default client; diff --git a/src/client/ironic/index.js b/src/client/ironic/index.js new file mode 100644 index 00000000..b54adf71 --- /dev/null +++ b/src/client/ironic/index.js @@ -0,0 +1,96 @@ +// 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 Base from '../client/base'; +import { ironicBase } from '../client/constants'; + +class IronicClient extends Base { + get baseUrl() { + return ironicBase(); + } + + get resources() { + return [ + { + key: 'nodes', + responseKey: 'node', + extendOperations: [ + { + name: 'updateStatesProvision', + key: 'states/provision', + method: 'put', + }, + { + name: 'UpdateStatesPower', + key: 'states/power', + method: 'put', + }, + { + name: 'updateMaintenance', + key: 'maintenance', + method: 'put', + }, + { + name: 'deleteMaintenance', + key: 'maintenance', + method: 'delete', + }, + { + name: 'getManagementBootDevice', + key: 'management/boot_device', + }, + { + name: 'updateManagementBootDevice', + key: 'management/boot_device', + method: 'put', + }, + { + name: 'getManagementBootDeviceSupported', + key: 'management/boot_device/supported', + }, + { + key: 'updateTraits', + method: 'put', + }, + ], + subResources: [ + { + key: 'states', + }, + { + key: 'validate', + }, + { + key: 'ports', + }, + { + key: 'portgroups', + responseKey: 'portgroup', + }, + ], + }, + { + key: 'ports', + responseKey: 'port', + }, + { + key: 'portgroups', + responseKey: 'portgroup', + }, + ]; + } +} + +const ironicClient = new IronicClient(); +export default ironicClient; diff --git a/src/client/keystone/index.js b/src/client/keystone/index.js new file mode 100644 index 00000000..6578223c --- /dev/null +++ b/src/client/keystone/index.js @@ -0,0 +1,149 @@ +// 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 Base from '../client/base'; +import { keystoneBase } from '../client/constants'; + +class KeystoneClient extends Base { + get baseUrl() { + return keystoneBase(); + } + + get resources() { + return [ + { + name: 'catalog', + key: 'auth/catalog', + responseKey: 'catalog', + }, + { + key: 'projects', + responseKey: 'project', + extendOperations: [ + { + name: 'updateTags', + key: 'tags', + method: 'put', + }, + ], + subResources: [ + { + key: 'tags', + responseKey: 'tag', + }, + { + key: 'groups', + subResources: [ + { + key: 'roles', + }, + ], + }, + { + key: 'users', + subResources: [ + { + key: 'roles', + }, + ], + }, + ], + }, + { + key: 'domains', + responseKey: 'domain', + subResources: [ + { + key: 'groups', + subResources: [ + { + key: 'roles', + }, + ], + }, + { + key: 'users', + subResources: [ + { + key: 'roles', + }, + ], + }, + ], + }, + { + key: 'roles', + responseKey: 'role', + subResources: [ + { + key: 'implies', + }, + ], + }, + { + name: 'roleAssignments', + key: 'role_assignments', + }, + { + key: 'users', + responseKey: 'user', + subResources: [ + { + key: 'projects', + }, + { + key: 'groups', + }, + ], + extendOperations: [ + { + name: 'updatePassword', + key: 'password', + method: 'post', + }, + ], + }, + { + key: 'groups', + responseKey: 'group', + subResources: [ + { + key: 'users', + }, + ], + }, + { + name: 'systemGroups', + key: 'system/groups', + subResources: [ + { + key: 'roles', + }, + ], + }, + { + name: 'systemUsers', + key: 'system/users', + subResources: [ + { + key: 'roles', + }, + ], + }, + ]; + } +} + +const keystoneClient = new KeystoneClient(); +export default keystoneClient; diff --git a/src/client/neutron/index.js b/src/client/neutron/index.js new file mode 100644 index 00000000..1d7c085d --- /dev/null +++ b/src/client/neutron/index.js @@ -0,0 +1,201 @@ +// 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 Base from '../client/base'; +import { neutronBase } from '../client/constants'; + +class NeutronClient extends Base { + get baseUrl() { + return neutronBase(); + } + + get resources() { + return [ + { + key: 'networks', + responseKey: 'network', + subResources: [ + { + name: 'dhcpAgents', + key: 'dhcp-agents', + }, + ], + }, + { + key: 'subnets', + responseKey: 'subnet', + }, + { + key: 'extensions', + }, + { + key: 'ports', + responseKey: 'port', + }, + { + key: 'routers', + responseKey: 'router', + extendOperations: [ + { + name: 'addRouterInterface', + key: 'add_router_interface', + method: 'put', + }, + { + name: 'removeRouterInterface', + key: 'remove_router_interface', + method: 'put', + }, + { + name: 'addExtraRoutes', + key: 'add_extraroutes', + method: 'put', + }, + { + name: 'removeExtraRoutes', + key: 'remove_extraroutes', + method: 'put', + }, + ], + }, + { + key: 'floatingips', + responseKey: 'floatingip', + subResources: [ + { + name: 'portForwardings', + key: 'port_forwardings', + responseKey: 'port_forwarding', + }, + ], + }, + { + key: 'agents', + responseKey: 'agent', + subResources: [ + { + name: 'dhcpNetworks', + key: 'dhcp-networks', + responseKey: 'network', + }, + { + name: 'l3Routers', + key: 'l3-routers', + responseKey: 'router', + }, + ], + }, + { + name: 'firewalls', + key: 'fwaas/firewall_groups', + responseKey: 'firewall_group', + }, + { + name: 'firewallPolicies', + key: 'fwaas/firewall_policies', + responseKey: 'firewall_policy', + extendOperations: [ + { + name: 'insertRule', + key: 'insert_rule', + method: 'put', + }, + { + name: 'removeRule', + key: 'remove_rule', + method: 'put', + }, + ], + }, + { + name: 'firewallRules', + key: 'fwaas/firewall_rules', + responseKey: 'firewall_rule', + }, + { + name: 'networkIpAvailabilities', + key: 'network-ip-availabilities', + }, + { + name: 'azones', + key: 'availability_zones', + }, + { + name: 'qosPolicies', + key: 'qos/policies', + responseKey: 'policy', + subResources: [ + { + name: 'bandwidthLimitRules', + key: 'bandwidth_limit_rules', + }, + { + name: 'dscpMarkingRules', + key: 'dscp_marking_rules', + }, + ], + }, + { + name: 'securityGroups', + key: 'security-groups', + responseKey: 'security_group', + }, + { + name: 'securityGroupRules', + key: 'security-group-rules', + responseKey: 'security_group_rule', + }, + { + key: 'subnets', + responseKey: 'subnet', + }, + { + name: 'endpointGroups', + key: 'endpoint-groups', + responseKey: 'endpoint_group', + }, + { + name: 'ikePolicies', + key: 'ikepolicies', + responseKey: 'ikepolicy', + }, + { + name: 'ipsecPolicies', + key: 'ipsecpolicies', + responseKey: 'ipsecpolicy', + }, + { + name: 'ipsecSiteConnections', + key: 'ipsec_site_connections', + responseKey: 'ipsec_site_connection', + }, + { + key: 'vpnservices', + responseKey: 'vpnservice', + }, + { + key: 'quotas', + responseKey: 'quota', + extendOperations: [ + { + key: 'details', + }, + ], + }, + ]; + } +} + +const neutronClient = new NeutronClient(); +export default neutronClient; diff --git a/src/client/nova/index.js b/src/client/nova/index.js new file mode 100644 index 00000000..f05ef111 --- /dev/null +++ b/src/client/nova/index.js @@ -0,0 +1,134 @@ +// 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 Base from '../client/base'; +import { novaBase } from '../client/constants'; + +class NovaClient extends Base { + get baseUrl() { + return novaBase(); + } + + get resources() { + return [ + { + key: 'servers', + responseKey: 'server', + subResources: [ + { + name: 'interfaces', + key: 'os-interface', + }, + { + name: 'volumeAttachments', + key: 'os-volume_attachments', + responseKey: 'volumeAttachment', + }, + { + name: 'instanceActions', + key: 'os-instance-actions', + responseKey: 'instanceAction', + }, + ], + extendOperations: [ + { + name: 'createConsole', + key: 'remote-consoles', + method: 'post', + }, + { + key: 'action', + method: 'post', + }, + ], + }, + { + name: 'zone', + key: 'os-availability-zone', + responseKey: 'availabilityZoneInfo', + }, + { + key: 'flavors', + responseKey: 'flavor', + extendOperations: [ + { + name: 'action', + key: 'action', + method: 'post', + }, + ], + subResources: [ + { + name: 'access', + key: 'os-flavor-access', + }, + { + name: 'extraSpecs', + key: 'os-extra_specs', + }, + ], + }, + { + name: 'keypairs', + key: 'os-keypairs', + responseKey: 'keypair', + }, + { + name: 'serverGroups', + key: 'os-server-groups', + responseKey: 'server_group', + }, + { + name: 'aggregates', + key: 'os-aggregates', + responseKey: 'aggregate', + extendOperations: [ + { + name: 'action', + key: 'action', + method: 'post', + }, + ], + }, + { + name: 'services', + key: 'os-services', + responseKey: 'service', + }, + { + name: 'quotaSets', + key: 'os-quota-sets', + responseKey: 'quota_set', + extendOperations: [ + { + key: 'detail', + }, + ], + }, + { + name: 'hypervisors', + key: 'os-hypervisors', + responseKey: 'hypervisor', + }, + { + name: 'pciDevices', + key: 'os-pci-devices', + responseKey: 'pci_device', + }, + ]; + } +} + +const novaClient = new NovaClient(); +export default novaClient; diff --git a/src/client/octavia/index.js b/src/client/octavia/index.js new file mode 100644 index 00000000..b10e789d --- /dev/null +++ b/src/client/octavia/index.js @@ -0,0 +1,62 @@ +// 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 Base from '../client/base'; +import { octaviaBase } from '../client/constants'; + +class OctaviaClient extends Base { + get baseUrl() { + return octaviaBase(); + } + + get resources() { + return [ + { + name: 'healthMonitors', + key: 'lbaas/healthmonitors', + responseKey: 'healthmonitor', + }, + { + name: 'listeners', + key: 'lbaas/listeners', + responseKey: 'listener', + }, + { + name: 'loadbalancers', + key: 'lbaas/loadbalancers', + responseKey: 'loadbalancer', + }, + { + name: 'pools', + key: 'lbaas/pools', + responseKey: 'pool', + extendOperations: [ + { + name: 'batchUpdateMembers', + key: 'members', + method: 'put', + }, + ], + subResources: [ + { + key: 'members', + }, + ], + }, + ]; + } +} + +const octaviaClient = new OctaviaClient(); +export default octaviaClient; diff --git a/src/client/placement/index.js b/src/client/placement/index.js new file mode 100644 index 00000000..7e522830 --- /dev/null +++ b/src/client/placement/index.js @@ -0,0 +1,42 @@ +// 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 Base from '../client/base'; +import { placementBase } from '../client/constants'; + +class PlacementClient extends Base { + get baseUrl() { + return placementBase(); + } + + get resources() { + return [ + { + name: 'resourceProviders', + key: 'resource_providers', + subResources: [ + { + key: 'inventories', + }, + ], + }, + { + key: 'traits', + }, + ]; + } +} + +const placementClient = new PlacementClient(); +export default placementClient; diff --git a/src/client/skyline/index.js b/src/client/skyline/index.js new file mode 100644 index 00000000..9d4055cb --- /dev/null +++ b/src/client/skyline/index.js @@ -0,0 +1,123 @@ +// 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 Base from '../client/base'; +import { skylineBase } from '../client/constants'; + +class SkylineClient extends Base { + get baseUrl() { + return skylineBase(); + } + + get resources() { + return [ + { + key: 'contrib', + isResource: false, + extendOperations: [ + { + key: 'domains', + }, + { + key: 'regions', + }, + { + name: 'keystoneEndpoints', + key: 'keystone_endpoints', + }, + ], + }, + { + key: 'extension', + isResource: false, + extendOperations: [ + { + key: 'servers', + }, + { + name: 'recycleServers', + key: 'recycle_servers', + }, + { + key: 'volumes', + }, + { + name: 'volumeSnapshots', + key: 'volume_snapshots', + }, + { + key: 'ports', + }, + ], + }, + { + name: 'policies', + key: 'policies', + extendOperations: [ + { + name: 'check', + key: 'check', + isDetail: false, + }, + ], + }, + { + name: '', + key: '', + isResource: false, + extendOperations: [ + { + key: 'login', + method: 'post', + }, + { + key: 'logout', + method: 'post', + }, + { + key: 'profile', + }, + { + name: 'switchProject', + method: 'post', + generate: (projectId, domainId) => { + const url = `switch_project/${projectId}`; + const data = { + project_id: projectId, + project_domain_id: domainId, + }; + const params = { + project_domain_id: domainId, + }; + return this.request.post(url, data, params); + }, + }, + ], + }, + { + key: 'setting', + responseKey: 'setting', + extendOperations: [ + { + key: 'list', + url: () => 'settings', + }, + ], + }, + ]; + } +} + +const skylineClient = new SkylineClient(); +export default skylineClient; diff --git a/src/components/Form/index.jsx b/src/components/Form/index.jsx index e90950b2..a28234eb 100644 --- a/src/components/Form/index.jsx +++ b/src/components/Form/index.jsx @@ -104,6 +104,11 @@ export default class BaseForm extends React.Component { return ''; } + get currentUser() { + const { user } = this.props.rootStore || {}; + return user || {}; + } + get isAdminPage() { const { pathname = '' } = this.props.location || {}; return isAdminPage(pathname); @@ -114,11 +119,11 @@ export default class BaseForm extends React.Component { } get currentProjectId() { - return globals.user.project.id; + return this.props.rootStore.projectId; } get currentProjectName() { - return globals.user.project.name; + return this.props.rootStore.projectName; } getUrl(path, adminStr) { @@ -258,7 +263,7 @@ export default class BaseForm extends React.Component { this.responseError = err; this.showNotice && Notify.errorWithDetail(err, this.errorText); // eslint-disable-next-line no-console - console.log(err); + console.log('err', err); if (callback && isFunction(callback)) { callback(false, true); } diff --git a/src/components/FormItem/NetworkSelectTable/index.jsx b/src/components/FormItem/NetworkSelectTable/index.jsx index 9ac5d90f..d6926e91 100644 --- a/src/components/FormItem/NetworkSelectTable/index.jsx +++ b/src/components/FormItem/NetworkSelectTable/index.jsx @@ -44,7 +44,7 @@ export default class NetworkSelectTable extends Component { } get currentProjectId() { - return globals.user.project.id; + return this.props.rootStore.projectId; } get hasAdminRole() { diff --git a/src/components/FormItem/VolumeSelectTable/index.jsx b/src/components/FormItem/VolumeSelectTable/index.jsx index 9e17f95e..75020075 100644 --- a/src/components/FormItem/VolumeSelectTable/index.jsx +++ b/src/components/FormItem/VolumeSelectTable/index.jsx @@ -34,7 +34,7 @@ export default class VolumeSelectTable extends Component { } get currentProjectId() { - return globals.user.project.id; + return this.props.rootStore.projectId; } get hasAdminRole() { diff --git a/src/components/Layout/GlobalHeader/AvatarDropdown.jsx b/src/components/Layout/GlobalHeader/AvatarDropdown.jsx index 1acff3fc..dfb42d6b 100644 --- a/src/components/Layout/GlobalHeader/AvatarDropdown.jsx +++ b/src/components/Layout/GlobalHeader/AvatarDropdown.jsx @@ -126,7 +126,7 @@ class AvatarDropdown extends React.Component { diff --git a/src/components/Layout/GlobalHeader/RightContent.jsx b/src/components/Layout/GlobalHeader/RightContent.jsx index 08ad311c..db87ee6f 100644 --- a/src/components/Layout/GlobalHeader/RightContent.jsx +++ b/src/components/Layout/GlobalHeader/RightContent.jsx @@ -28,7 +28,7 @@ const gotoConsole = (type, props) => { }; const GlobalHeaderRight = (props) => { - const { isAdminPage = false, rootStore: { hasAdminRole = false } = {} } = + const { isAdminPage = false, rootStore: { hasAdminPageRole = false } = {} } = props; let linkRender = null; if (isAdminPage) { @@ -41,7 +41,7 @@ const GlobalHeaderRight = (props) => { {t('Console')} ); - } else if (hasAdminRole) { + } else if (hasAdminPageRole) { linkRender = (