Просмотр исходного кода

Add provider logos UI modding support

Includes an updated `README.md` file with modding instructions and a
sample modding file.
Sergiu Miclea 6 лет назад
Родитель
Сommit
c71fa17a7a
67 измененных файлов с 248 добавлено и 210 удалено
  1. 20 8
      README.md
  2. 43 0
      server/api/DownloadZipApi.js
  3. 110 0
      server/api/LogosApi.js
  4. 0 0
      server/api/resources/providerLogos/aws-128-disabled.svg
  5. 0 0
      server/api/resources/providerLogos/aws-128.svg
  6. 0 0
      server/api/resources/providerLogos/aws-32-white.svg
  7. 0 0
      server/api/resources/providerLogos/aws-32.svg
  8. 0 0
      server/api/resources/providerLogos/aws-42.svg
  9. 0 0
      server/api/resources/providerLogos/aws-64.svg
  10. 0 0
      server/api/resources/providerLogos/azure-128-disabled.svg
  11. 0 0
      server/api/resources/providerLogos/azure-128.svg
  12. 0 0
      server/api/resources/providerLogos/azure-32-white.svg
  13. 0 0
      server/api/resources/providerLogos/azure-32.svg
  14. 0 0
      server/api/resources/providerLogos/azure-42.svg
  15. 0 0
      server/api/resources/providerLogos/azure-64.svg
  16. 0 0
      server/api/resources/providerLogos/hyper-v-128-disabled.svg
  17. 0 0
      server/api/resources/providerLogos/hyper-v-128.svg
  18. 0 0
      server/api/resources/providerLogos/hyper-v-32-white.svg
  19. 0 0
      server/api/resources/providerLogos/hyper-v-32.svg
  20. 0 0
      server/api/resources/providerLogos/hyper-v-42.svg
  21. 0 0
      server/api/resources/providerLogos/hyper-v-64.svg
  22. 0 0
      server/api/resources/providerLogos/oci-128-disabled.svg
  23. 0 0
      server/api/resources/providerLogos/oci-128.svg
  24. 0 0
      server/api/resources/providerLogos/oci-32-white.svg
  25. 0 0
      server/api/resources/providerLogos/oci-32.svg
  26. 0 0
      server/api/resources/providerLogos/oci-42.svg
  27. 0 0
      server/api/resources/providerLogos/oci-64.svg
  28. 0 0
      server/api/resources/providerLogos/opc-128-disabled.svg
  29. 0 0
      server/api/resources/providerLogos/opc-128.svg
  30. 0 0
      server/api/resources/providerLogos/opc-32-white.svg
  31. 0 0
      server/api/resources/providerLogos/opc-32.svg
  32. 0 0
      server/api/resources/providerLogos/opc-42.svg
  33. 0 0
      server/api/resources/providerLogos/opc-64.svg
  34. 0 0
      server/api/resources/providerLogos/openstack-128-disabled.svg
  35. 0 0
      server/api/resources/providerLogos/openstack-128.svg
  36. 0 0
      server/api/resources/providerLogos/openstack-32-white.svg
  37. 0 0
      server/api/resources/providerLogos/openstack-32.svg
  38. 0 0
      server/api/resources/providerLogos/openstack-42.svg
  39. 0 0
      server/api/resources/providerLogos/openstack-64.svg
  40. 0 0
      server/api/resources/providerLogos/oracle_vm-128-disabled.svg
  41. 0 0
      server/api/resources/providerLogos/oracle_vm-128.svg
  42. 0 0
      server/api/resources/providerLogos/oracle_vm-32-white.svg
  43. 0 0
      server/api/resources/providerLogos/oracle_vm-32.svg
  44. 0 0
      server/api/resources/providerLogos/oracle_vm-42.svg
  45. 0 0
      server/api/resources/providerLogos/oracle_vm-64.svg
  46. 0 0
      server/api/resources/providerLogos/scvmm-128-disabled.svg
  47. 0 0
      server/api/resources/providerLogos/scvmm-128.svg
  48. 0 0
      server/api/resources/providerLogos/scvmm-32-white.svg
  49. 0 0
      server/api/resources/providerLogos/scvmm-32.svg
  50. 0 0
      server/api/resources/providerLogos/scvmm-42.svg
  51. 0 0
      server/api/resources/providerLogos/scvmm-64.svg
  52. 0 0
      server/api/resources/providerLogos/vmware_vsphere-128-disabled.svg
  53. 0 0
      server/api/resources/providerLogos/vmware_vsphere-128.svg
  54. 0 0
      server/api/resources/providerLogos/vmware_vsphere-32-white.svg
  55. 0 0
      server/api/resources/providerLogos/vmware_vsphere-32.svg
  56. 0 0
      server/api/resources/providerLogos/vmware_vsphere-42.svg
  57. 0 0
      server/api/resources/providerLogos/vmware_vsphere-64.svg
  58. 6 27
      server/api/router.js
  59. 1 1
      server/main.js
  60. 14 167
      src/components/atoms/EndpointLogos/EndpointLogos.jsx
  61. 0 0
      src/components/atoms/EndpointLogos/resources/Generic.jsx
  62. 0 0
      src/components/atoms/EndpointLogos/resources/generic-128-disabled.svg
  63. 0 0
      src/components/atoms/EndpointLogos/resources/generic-128.svg
  64. 0 0
      src/components/atoms/EndpointLogos/resources/generic-64.svg
  65. 9 1
      src/components/atoms/EndpointLogos/story.jsx
  66. 2 6
      src/components/atoms/EndpointLogos/test.jsx
  67. 43 0
      ui-mod-sample.json

+ 20 - 8
README.md

@@ -2,31 +2,43 @@
 
 Web  GUI for [coriolis](https://github.com/cloudbase/coriolis)
 
-![](https://github.com/cloudbase/coriolis-web/workflows/Master/badge.svg) [![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
-   
-### Install instructions
+![CI Badge](https://github.com/cloudbase/coriolis-web/workflows/Master/badge.svg) [![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
+
+## Install instructions
+
 - [node](https://nodejs.org/en/download/package-manager/) >=6.x and [yarn](https://yarnpkg.com/lang/en/docs/install/) are required
 - clone repo
 - run `yarn install` or `yarn install --production` to install packages and dependencies for development or production mode
 - change the `coriolisUrl` variable in ./src/config.js to match the Coriolis Server path
 
+## Build instructions
 
-### Build instructions
 - run `yarn build`
 - run `node server.js` to start the server
 
-Your server will be running at http://localhost:3000/
+Your server will be running at `http://localhost:3000/`
 
-### Testing
+## Testing
 
 - unit tests can be run using `yarn test`
 - e2e integration tests can be run using `yarn cypress`. First though, you have to create the `private/cypress/config.js` file using `private/cypress/config.template.js` as a template and then run `yarn build` and `node server`.
 
-### Development mode
+## Development mode
+
 - run `yarn start` to start local development server
 
-Your development server will be running at http://localhost:3000/
+Your development server will be running at `http://localhost:3000/`
 
 This should be used only for development, as it contains live-reload and other development tools.
 
 You can view some of the UIs components in the [Storybook](https://github.com/storybooks/storybook) by running `yarn storybook`
+
+## Modding
+
+The UI can be modded externally using a `.json` modding file. A sample is available in the repo [`ui-mod-sample.json`](ui-mod-sample.json).
+
+The path to the .json mod file needs to be set in `MOD_JSON` environment variable (ex.: `MOD_JSON=/usr/ui-mod.json`).
+
+Any provider logos can be replaced using local logo images. The local image file paths need to be absolute.
+
+You can specify one logo, in which case it will be scaled to all sizes. You can also specify logos for just a couple of the sizes, in which case the closest size to the one required will be used. Open [`ui-mod-sample.json`](ui-mod-sample.json) for more details.

+ 43 - 0
server/api/DownloadZipApi.js

@@ -0,0 +1,43 @@
+/*
+Copyright (C) 2019  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 AdmZip from 'adm-zip'
+import stream from 'stream'
+
+import type { ZipContent } from '../../src/types/ZipContent'
+
+export default (router: any) => {
+  router.post('/download-zip', (req, res) => {
+    try {
+      let contents: ZipContent[] = req.body.contents
+      if (!contents || !contents.length || !contents[0].filename || typeof contents[0].content !== 'string') {
+        throw new Error()
+      }
+      let zip = new AdmZip()
+      contents.forEach(content => {
+        zip.addFile(content.filename, Buffer.alloc(content.content.length, content.content))
+      })
+      let zipBuffer = zip.toBuffer()
+      let readStream = new stream.PassThrough()
+      readStream.end(zipBuffer)
+      res.set('Content-Disposition', 'attachment; filename=contents.zip')
+      res.set('Content-Type', 'text/plain')
+      readStream.pipe(res)
+    } catch (err) {
+      console.error(err)
+      res.status(500).json({ error: { message: 'Invalid request body for download zip API' } })
+    }
+  })
+}

+ 110 - 0
server/api/LogosApi.js

@@ -0,0 +1,110 @@
+/*
+Copyright (C) 2019  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 path from 'path'
+import fs from 'fs'
+
+const getModJsonProviders = (jsonPath: string) => {
+  let jsonContent = fs.readFileSync(jsonPath)
+  let json = JSON.parse(jsonContent)
+  if (!json.providers) {
+    throw new Error()
+  }
+  return json.providers
+}
+
+const getOptimalLogoHeightKey = (
+  availableHeightKeys: string[],
+  requestedHeight: number,
+  style: ?string
+): string => {
+  let heightKeys = availableHeightKeys
+  if (style) {
+    let styledKeys = heightKeys.filter(k => style ? k.indexOf(style) > -1 : false)
+    if (styledKeys.length) {
+      heightKeys = styledKeys
+    }
+  }
+
+  let optimal = heightKeys.reduce((prev, curr) => {
+    let prevHeight = /d+/.exec(prev)
+    let currHeight = /d+/.exec(curr)
+    prevHeight = prevHeight ? Number(prevHeight[0]) : 0
+    currHeight = currHeight ? Number(currHeight[0]) : 0
+    return Math.abs(currHeight - requestedHeight) < Math.abs(prevHeight - requestedHeight) ? curr : prev
+  })
+  return optimal
+}
+
+export default (router: express$Router) => {
+  // $FlowIgnore
+  router.get('/logos/:provider/:size/:style?', (req, res) => {
+    const SIZES = [32, 42, 64, 128]
+    const STYLES = ['white', 'disabled']
+    let { provider, size, style } = req.params
+    size = Number(size)
+
+    if (SIZES.indexOf(size) === -1) {
+      res.status(400).json({ error: { message: `Valid sizes are: ${SIZES.join(', ')}` } })
+      return
+    }
+    if (style && STYLES.indexOf(style) === -1) {
+      res.status(400).json({ error: { message: `Valid styles are: ${STYLES.join(', ')}` } })
+      return
+    }
+    let logoBase = path.join(__dirname, '/resources/providerLogos')
+    let logoPath = `${logoBase}/${provider}-${size}`
+    logoPath = style ? `${logoPath}-${style}.svg` : `${logoPath}.svg`
+
+    let modJsonPath: ?string = process.env.MOD_JSON
+    if (!modJsonPath) {
+      res.sendFile(logoPath)
+      return
+    }
+
+    try {
+      let providersJson = getModJsonProviders(modJsonPath)
+      let providerJson = providersJson[provider]
+      if (!providerJson) {
+        res.sendFile(logoPath)
+        return
+      }
+      let providerLogosJson = providerJson.logos
+      if (!providerLogosJson) {
+        console.log(`No logos specified in MOD_JSON file for '${provider}' provider`)
+        res.sendFile(logoPath)
+        return
+      }
+      let providerLogosKeys = Object.keys(providerLogosJson)
+      if (!providerLogosKeys.length) {
+        console.log(`No logo heights specified in MOD_JSON file for '${provider}' provider`)
+        res.sendFile(logoPath)
+        return
+      }
+      let optimalHeightKey = getOptimalLogoHeightKey(providerLogosKeys, size, style)
+      let modLogoPath = providerLogosJson[optimalHeightKey].path
+      if (!modLogoPath) {
+        console.log(`No logo path specified in MOD_JSON file for '${provider}' provider`)
+        res.sendFile(logoPath)
+        return
+      }
+      res.sendFile(modLogoPath)
+    } catch (err) {
+      console.error(err)
+      res.status(500).json({ error: { message: 'Invalid Mod JSON file' } })
+    }
+  })
+}

+ 0 - 0
src/components/atoms/EndpointLogos/images/aws-128-disabled.svg → server/api/resources/providerLogos/aws-128-disabled.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/aws-128.svg → server/api/resources/providerLogos/aws-128.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/aws-32-white.svg → server/api/resources/providerLogos/aws-32-white.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/aws-32.svg → server/api/resources/providerLogos/aws-32.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/aws-42.svg → server/api/resources/providerLogos/aws-42.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/aws-64.svg → server/api/resources/providerLogos/aws-64.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/azure-128-disabled.svg → server/api/resources/providerLogos/azure-128-disabled.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/azure-128.svg → server/api/resources/providerLogos/azure-128.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/azure-32-white.svg → server/api/resources/providerLogos/azure-32-white.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/azure-32.svg → server/api/resources/providerLogos/azure-32.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/azure-42.svg → server/api/resources/providerLogos/azure-42.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/azure-64.svg → server/api/resources/providerLogos/azure-64.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/hyperv-128-disabled.svg → server/api/resources/providerLogos/hyper-v-128-disabled.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/hyperv-128.svg → server/api/resources/providerLogos/hyper-v-128.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/hyperv-32-white.svg → server/api/resources/providerLogos/hyper-v-32-white.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/hyperv-32.svg → server/api/resources/providerLogos/hyper-v-32.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/hyperv-42.svg → server/api/resources/providerLogos/hyper-v-42.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/hyperv-64.svg → server/api/resources/providerLogos/hyper-v-64.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/oci-128-disabled.svg → server/api/resources/providerLogos/oci-128-disabled.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/oci-128.svg → server/api/resources/providerLogos/oci-128.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/oci-32-white.svg → server/api/resources/providerLogos/oci-32-white.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/oci-32.svg → server/api/resources/providerLogos/oci-32.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/oci-42.svg → server/api/resources/providerLogos/oci-42.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/oci-64.svg → server/api/resources/providerLogos/oci-64.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/opc-128-disabled.svg → server/api/resources/providerLogos/opc-128-disabled.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/opc-128.svg → server/api/resources/providerLogos/opc-128.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/opc-32-white.svg → server/api/resources/providerLogos/opc-32-white.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/opc-32.svg → server/api/resources/providerLogos/opc-32.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/opc-42.svg → server/api/resources/providerLogos/opc-42.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/opc-64.svg → server/api/resources/providerLogos/opc-64.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/openstack-128-disabled.svg → server/api/resources/providerLogos/openstack-128-disabled.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/openstack-128.svg → server/api/resources/providerLogos/openstack-128.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/openstack-32-white.svg → server/api/resources/providerLogos/openstack-32-white.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/openstack-32.svg → server/api/resources/providerLogos/openstack-32.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/openstack-42.svg → server/api/resources/providerLogos/openstack-42.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/openstack-64.svg → server/api/resources/providerLogos/openstack-64.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/oraclevm-128-disabled.svg → server/api/resources/providerLogos/oracle_vm-128-disabled.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/oraclevm-128.svg → server/api/resources/providerLogos/oracle_vm-128.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/oraclevm-32-white.svg → server/api/resources/providerLogos/oracle_vm-32-white.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/oraclevm-32.svg → server/api/resources/providerLogos/oracle_vm-32.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/oraclevm-42.svg → server/api/resources/providerLogos/oracle_vm-42.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/oraclevm-64.svg → server/api/resources/providerLogos/oracle_vm-64.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/scvmm-128-disabled.svg → server/api/resources/providerLogos/scvmm-128-disabled.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/scvmm-128.svg → server/api/resources/providerLogos/scvmm-128.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/scvmm-32-white.svg → server/api/resources/providerLogos/scvmm-32-white.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/scvmm-32.svg → server/api/resources/providerLogos/scvmm-32.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/scvmm-42.svg → server/api/resources/providerLogos/scvmm-42.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/scvmm-64.svg → server/api/resources/providerLogos/scvmm-64.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/vmware-128-disabled.svg → server/api/resources/providerLogos/vmware_vsphere-128-disabled.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/vmware-128.svg → server/api/resources/providerLogos/vmware_vsphere-128.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/vmware-32-white.svg → server/api/resources/providerLogos/vmware_vsphere-32-white.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/vmware-32.svg → server/api/resources/providerLogos/vmware_vsphere-32.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/vmware-42.svg → server/api/resources/providerLogos/vmware_vsphere-42.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/vmware-64.svg → server/api/resources/providerLogos/vmware_vsphere-64.svg


+ 6 - 27
server/router.js → server/api/router.js

@@ -17,12 +17,11 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 import requireWithoutCache from 'require-without-cache'
 import express from 'express'
 import bodyParser from 'body-parser'
-import AdmZip from 'adm-zip'
-import stream from 'stream'
 
-import packageJson from '../package.json'
+import LogosApi from './LogosApi'
+import DownloadZipApi from './DownloadZipApi'
 
-import type { ZipContent } from '../src/types/ZipContent'
+import packageJson from '../../package.json'
 
 const router = express.Router()
 
@@ -35,30 +34,10 @@ router.get('/version', (req, res) => {
 
 // $FlowIgnore
 router.get('/config', (req, res) => {
-  res.send(requireWithoutCache('../config.js', require).config)
+  res.send(requireWithoutCache('../../config.js', require).config)
 })
 
-// $FlowIgnore
-router.post('/download-zip', (req, res) => {
-  try {
-    let contents: ZipContent[] = req.body.contents
-    if (!contents || !contents.length || !contents[0].filename || typeof contents[0].content !== 'string') {
-      throw new Error()
-    }
-    let zip = new AdmZip()
-    contents.forEach(content => {
-      zip.addFile(content.filename, Buffer.alloc(content.content.length, content.content))
-    })
-    let zipBuffer = zip.toBuffer()
-    let readStream = new stream.PassThrough()
-    readStream.end(zipBuffer)
-    res.set('Content-Disposition', 'attachment; filename=contents.zip')
-    res.set('Content-Type', 'text/plain')
-    readStream.pipe(res)
-  } catch (err) {
-    console.error(err)
-    res.status(500).json({ error: { message: 'Invalid request body for download zip API' } })
-  }
-})
+DownloadZipApi(router)
+LogosApi(router)
 
 export default router

+ 1 - 1
server/main.js

@@ -18,7 +18,7 @@ import express from 'express'
 import fs from 'fs'
 import path from 'path'
 
-import router from './router'
+import router from './api/router'
 
 // Create our app
 const app = express()

+ 14 - 167
src/components/atoms/EndpointLogos/EndpointLogos.jsx

@@ -18,142 +18,8 @@ import React from 'react'
 import { observer } from 'mobx-react'
 import styled, { css } from 'styled-components'
 
-import Generic from './images/Generic'
+import Generic from './resources/Generic'
 
-import aws32Image from './images/aws-32.svg'
-import azure32Image from './images/azure-32.svg'
-import opc32Image from './images/opc-32.svg'
-import openstack32Image from './images/openstack-32.svg'
-import oraclevm32Image from './images/oraclevm-32.svg'
-import vmware32Image from './images/vmware-32.svg'
-import oci32Image from './images/oci-32.svg'
-import hyperv32Image from './images/hyperv-32.svg'
-import scvmm32Image from './images/scvmm-32.svg'
-
-import aws32WhiteImage from './images/aws-32-white.svg'
-import azure32WhiteImage from './images/azure-32-white.svg'
-import opc32WhiteImage from './images/opc-32-white.svg'
-import openstack32WhiteImage from './images/openstack-32-white.svg'
-import oraclevm32WhiteImage from './images/oraclevm-32-white.svg'
-import vmware32WhiteImage from './images/vmware-32-white.svg'
-import oci32WhiteImage from './images/oci-32-white.svg'
-import hyperv32WhiteImage from './images/hyperv-32-white.svg'
-import scvmm32WhiteImage from './images/scvmm-32-white.svg'
-
-import aws42Image from './images/aws-42.svg'
-import azure42Image from './images/azure-42.svg'
-import opc42Image from './images/opc-42.svg'
-import openstack42Image from './images/openstack-42.svg'
-import oraclevm42Image from './images/oraclevm-42.svg'
-import vmware42Image from './images/vmware-42.svg'
-import oci42Image from './images/oci-42.svg'
-import hyperv42Image from './images/hyperv-42.svg'
-import scvmm42Image from './images/scvmm-42.svg'
-
-import aws64Image from './images/aws-64.svg'
-import azure64Image from './images/azure-64.svg'
-import opc64Image from './images/opc-64.svg'
-import openstack64Image from './images/openstack-64.svg'
-import oraclevm64Image from './images/oraclevm-64.svg'
-import vmware64Image from './images/vmware-64.svg'
-import oci64Image from './images/oci-64.svg'
-import hyperv64Image from './images/hyperv-64.svg'
-import scvmm64Image from './images/scvmm-64.svg'
-
-import aws128Image from './images/aws-128.svg'
-import azure128Image from './images/azure-128.svg'
-import opc128Image from './images/opc-128.svg'
-import openstack128Image from './images/openstack-128.svg'
-import oraclevm128Image from './images/oraclevm-128.svg'
-import vmware128Image from './images/vmware-128.svg'
-import oci128Image from './images/oci-128.svg'
-import hyperv128Image from './images/hyperv-128.svg'
-import scvmm128Image from './images/scvmm-128.svg'
-
-import aws128DisabledImage from './images/aws-128-disabled.svg'
-import azure128DisabledImage from './images/azure-128-disabled.svg'
-import opc128DisabledImage from './images/opc-128-disabled.svg'
-import openstack128DisabledImage from './images/openstack-128-disabled.svg'
-import oraclevm128DisabledImage from './images/oraclevm-128-disabled.svg'
-import vmware128DisabledImage from './images/vmware-128-disabled.svg'
-import oci128DisabledImage from './images/oci-128-disabled.svg'
-import hyperv128DisabledImage from './images/hyperv-128-disabled.svg'
-import scvmm128DisabledImage from './images/scvmm-128-disabled.svg'
-
-const endpointImages = {
-  azure: [
-    { h: 32, image: azure32Image },
-    { h: 32, image: azure32WhiteImage, white: true },
-    { h: 42, image: azure42Image },
-    { h: 64, image: azure64Image },
-    { h: 128, image: azure128Image },
-    { h: 128, image: azure128DisabledImage, disabled: true },
-  ],
-  openstack: [
-    { h: 32, image: openstack32Image },
-    { h: 32, image: openstack32WhiteImage, white: true },
-    { h: 42, image: openstack42Image },
-    { h: 64, image: openstack64Image },
-    { h: 128, image: openstack128Image },
-    { h: 128, image: openstack128DisabledImage, disabled: true },
-  ],
-  opc: [
-    { h: 32, image: opc32Image },
-    { h: 32, image: opc32WhiteImage, white: true },
-    { h: 42, image: opc42Image },
-    { h: 64, image: opc64Image },
-    { h: 128, image: opc128Image },
-    { h: 128, image: opc128DisabledImage, disabled: true },
-  ],
-  oracle_vm: [
-    { h: 32, image: oraclevm32Image },
-    { h: 32, image: oraclevm32WhiteImage, white: true },
-    { h: 42, image: oraclevm42Image },
-    { h: 64, image: oraclevm64Image },
-    { h: 128, image: oraclevm128Image },
-    { h: 128, image: oraclevm128DisabledImage, disabled: true },
-  ],
-  vmware_vsphere: [
-    { h: 32, image: vmware32Image },
-    { h: 32, image: vmware32WhiteImage, white: true },
-    { h: 42, image: vmware42Image },
-    { h: 64, image: vmware64Image },
-    { h: 128, image: vmware128Image },
-    { h: 128, image: vmware128DisabledImage, disabled: true },
-  ],
-  aws: [
-    { h: 32, image: aws32Image },
-    { h: 32, image: aws32WhiteImage, white: true },
-    { h: 42, image: aws42Image },
-    { h: 64, image: aws64Image },
-    { h: 128, image: aws128Image },
-    { h: 128, image: aws128DisabledImage, disabled: true },
-  ],
-  oci: [
-    { h: 32, image: oci32Image },
-    { h: 32, image: oci32WhiteImage, white: true },
-    { h: 42, image: oci42Image },
-    { h: 64, image: oci64Image },
-    { h: 128, image: oci128Image },
-    { h: 128, image: oci128DisabledImage, disabled: true },
-  ],
-  'hyper-v': [
-    { h: 32, image: hyperv32Image },
-    { h: 32, image: hyperv32WhiteImage, white: true },
-    { h: 42, image: hyperv42Image },
-    { h: 64, image: hyperv64Image },
-    { h: 128, image: hyperv128Image },
-    { h: 128, image: hyperv128DisabledImage, disabled: true },
-  ],
-  scvmm: [
-    { h: 32, image: scvmm32Image },
-    { h: 32, image: scvmm32WhiteImage, white: true },
-    { h: 42, image: scvmm42Image },
-    { h: 64, image: scvmm64Image },
-    { h: 128, image: scvmm128Image },
-    { h: 128, image: scvmm128DisabledImage, disabled: true },
-  ],
-}
 const Wrapper = styled.div``
 const Logo = styled.div`
   display: flex;
@@ -161,7 +27,7 @@ const Logo = styled.div`
   justify-content: center;
   width: ${props => props.width}px;
   height: ${props => props.height}px;
-  ${props => props.imageInfo ? css`background: url('${props.imageInfo.image}') no-repeat center;` : ''}
+  ${props => props.url ? css`background: url('${props.url}') no-repeat center;` : ''}
 `
 const widthHeights = [
   { w: 80, h: 32 },
@@ -169,12 +35,15 @@ const widthHeights = [
   { w: 192, h: 128 },
   { w: 192, h: 64 },
 ]
-
+const PROVIDER_LOGOS = [
+  'azure', 'openstack', 'opc', 'oracle_vm', 'vmware_vsphere', 'aws', 'oci', 'hyper-v', 'scvmm',
+]
 type Props = {
   endpoint?: ?string,
   height: number,
   disabled?: boolean,
   white?: boolean,
+  baseUrl?: string,
   'data-test-id'?: string,
 }
 @observer
@@ -183,29 +52,6 @@ class EndpointLogos extends React.Component<Props> {
     height: 64,
   }
 
-  renderLogo(size: { w: number, h: number }) {
-    let imageInfo = null
-
-    if (this.props.endpoint && endpointImages[this.props.endpoint]) {
-      imageInfo = endpointImages[this.props.endpoint].find(
-        i => i.h === size.h && (!this.props.disabled || i.disabled === true) && (!this.props.white || i.white === true))
-    } else {
-      return null
-    }
-
-    if (!imageInfo) {
-      return null
-    }
-
-    return (
-      <Logo
-        width={size.w}
-        height={size.h}
-        imageInfo={imageInfo}
-      />
-    )
-  }
-
   renderGenericLogo(size: { w: number, h: number }) {
     return (
       <Generic
@@ -225,11 +71,12 @@ class EndpointLogos extends React.Component<Props> {
       return null
     }
 
-    let imageInfo = null
-
-    if (this.props.endpoint && endpointImages[this.props.endpoint]) {
-      imageInfo = endpointImages[this.props.endpoint].find(i => i.h === size.h && (!this.props.disabled || i.disabled === true)
-        && (!this.props.white || i.white === true))
+    let imageUrl: ?string = null
+    let provider = this.props.endpoint
+    if (provider && PROVIDER_LOGOS.indexOf(provider) > -1) {
+      imageUrl = `${this.props.baseUrl || ''}/api/logos/${provider}/${size.h}`
+      let style = this.props.white ? 'white' : this.props.disabled ? 'disabled' : null
+      imageUrl = style ? `${imageUrl}/${style}` : imageUrl
     }
 
     return (
@@ -237,10 +84,10 @@ class EndpointLogos extends React.Component<Props> {
         <Logo
           width={size.w}
           height={size.h}
-          imageInfo={imageInfo}
+          url={imageUrl}
           data-test-id="endpointLogos-logo"
         >
-          {imageInfo ? null : this.renderGenericLogo(size)}
+          {imageUrl ? null : this.renderGenericLogo(size)}
         </Logo>
       </Wrapper>
     )

+ 0 - 0
src/components/atoms/EndpointLogos/images/Generic.jsx → src/components/atoms/EndpointLogos/resources/Generic.jsx


+ 0 - 0
src/components/atoms/EndpointLogos/images/generic-128-disabled.svg → src/components/atoms/EndpointLogos/resources/generic-128-disabled.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/generic-128.svg → src/components/atoms/EndpointLogos/resources/generic-128.svg


+ 0 - 0
src/components/atoms/EndpointLogos/images/generic-64.svg → src/components/atoms/EndpointLogos/resources/generic-64.svg


+ 9 - 1
src/components/atoms/EndpointLogos/story.jsx

@@ -31,7 +31,15 @@ const Wrapper = styled.div`
     margin-top: 32px;
   }
 `
-const wrap = (endpoint, height, disabled = false, white = false) => <EndpointLogos endpoint={endpoint} height={height} disabled={disabled} white={white} />
+const wrap = (endpoint, height, disabled = false, white = false) => (
+  <EndpointLogos
+    endpoint={endpoint}
+    height={height}
+    disabled={disabled}
+    white={white}
+    baseUrl="http://localhost:3000"
+  />
+)
 let providers = [
   'aws',
   'azure',

+ 2 - 6
src/components/atoms/EndpointLogos/test.jsx

@@ -25,17 +25,13 @@ describe('EndpointLogos Component', () => {
   it('renders 32px aws', () => {
     const wrapper = wrap({ height: 32, endpoint: 'aws' })
     const logo = wrapper.find('logo')
-    expect(logo.prop('imageInfo').h).toBe(32)
-    expect(logo.prop('imageInfo').image).toBe('file')
-    expect(wrapper.prop('endpoint')).toBe('aws')
+    expect(logo.prop('url')).toBe('/api/logos/aws/32')
   })
 
   it('renders 128px azure disabled', () => {
     const wrapper = wrap({ height: 128, endpoint: 'azure', disabled: true })
     const logo = wrapper.find('logo')
-    expect(logo.prop('imageInfo').h).toBe(128)
-    expect(logo.prop('imageInfo').disabled).toBe(true)
-    expect(wrapper.prop('endpoint')).toBe('azure')
+    expect(logo.prop('url')).toBe('/api/logos/azure/128/disabled')
   })
 
   it('renders 64px generic logo', () => {

+ 43 - 0
ui-mod-sample.json

@@ -0,0 +1,43 @@
+{
+  "providers": {
+    "openstack": {
+      "logos": {
+        "height32": {
+          "_comment": "As seen in Wizard page breadcrumbs and Replicas page list item.",
+          "path": "/tmp/logos/logo.svg"
+        },
+        "height32white": {
+          "_comment": "As seen in Dashboard page, Top Endpoints chart tooltip.",
+          "path": "/tmp/logos/logo-white.svg"
+        },
+        "height42": {
+          "_comment": "As seen in Endpoints page list item.",
+          "path": "/tmp/logos/logo.svg"
+        },
+        "height64": {
+          "_comment": "As seen in Replica Details page and Endpoint Details page.",
+          "path": "/tmp/logos/logo.svg"
+        },
+        "height128": {
+          "_comment": "As seen in New Endpoint modal and Wizard endpoint selection page.",
+          "path": "/tmp/logos/logo.svg"
+        },
+        "height128disabled": {
+          "_comment": "As seen in Wizard endpoint selection page when there's no endpoint for that provider.",
+          "path": "/tmp/logos/logo-disabled.svg"
+        }
+      }
+    },
+    "oci": {
+      "_comment": "Specifying all heights is not required, it will scale to the closest one provided. If there's a no height properties in 'logos', the logo will be scaled to all heights",
+      "logos": {
+        "default": {
+          "path": "/tmp/logos/one-logo.svg"
+        },
+        "disabled": {
+          "path": "/tmp/logos/one-logo-disabled.svg"
+        }
+      }
+    }
+  }
+}