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

Use custom order and dynamic required fields

Use custom order for Openstack fields and set some fields' `required`
property based on other fields' value.
Sergiu Miclea 8 лет назад
Родитель
Сommit
4db4df08f0

+ 12 - 6
src/plugins/endpoint/default/SchemaPlugin.js

@@ -12,7 +12,12 @@ 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/>.
 */
 
-export const defaultSchemaToFields = schema => {
+// @flow
+
+import type { Schema } from '../../../types/Schema'
+import type { Field } from '../../../types/Field'
+
+export const defaultSchemaToFields = (schema: Schema) => {
   let fields = Object.keys(schema.properties).map(fieldName => {
     let field = {
       ...schema.properties[fieldName],
@@ -25,7 +30,7 @@ export const defaultSchemaToFields = schema => {
   return fields
 }
 
-export const connectionSchemaToFields = schema => {
+export const connectionSchemaToFields = (schema: Schema) => {
   let fields = defaultSchemaToFields(schema)
 
   let sortPriority = { username: 1, password: 2 }
@@ -45,12 +50,13 @@ export const connectionSchemaToFields = schema => {
   return fields
 }
 
-export const generateField = (name, label, required = false, type = 'string', defaultValue = null) => {
+export const generateField = (name: string, label: string, required: boolean = false, type: string = 'string', defaultValue: any = null) => {
   let field = {
     name,
     label,
     type,
     required,
+    default: undefined,
   }
 
   if (defaultValue) {
@@ -60,7 +66,7 @@ export const generateField = (name, label, required = false, type = 'string', de
   return field
 }
 
-export const fieldsToPayload = (data, schema) => {
+export const fieldsToPayload = (data: { [string]: mixed }, schema: Schema) => {
   let info = {}
 
   Object.keys(schema.properties).forEach(fieldName => {
@@ -73,7 +79,7 @@ export const fieldsToPayload = (data, schema) => {
 }
 
 export default class ConnectionSchemaParser {
-  static parseSchemaToFields(schema) {
+  static parseSchemaToFields(schema: Schema): Field[] {
     let fields = connectionSchemaToFields(schema.oneOf[0])
 
     fields = [
@@ -85,7 +91,7 @@ export default class ConnectionSchemaParser {
     return fields
   }
 
-  static parseFieldsToPayload(data, schema) {
+  static parseFieldsToPayload(data: { [string]: mixed }, schema: Schema) {
     let payload = {}
 
     payload.name = data.name

+ 2 - 0
src/plugins/endpoint/index.js

@@ -19,10 +19,12 @@ import AzureSchemaPlugin from './azure/SchemaPlugin'
 import DefaultContentPlugin from './default/ContentPlugin'
 import AzureContentPlugin from './azure/ContentPlugin'
 import OpenstackContentPlugin from './openstack/ContentPlugin'
+import OpenstackSchemaPlugin from './openstack/SchemaPlugin'
 
 export const SchemaPlugin = {
   default: DefaultSchemaPlugin,
   azure: AzureSchemaPlugin,
+  openstack: OpenstackSchemaPlugin,
 }
 
 export const ContentPlugin = {

+ 26 - 40
src/plugins/endpoint/openstack/ContentPlugin.jsx

@@ -12,49 +12,26 @@ 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/>.
 */
 
-import React from 'react'
-import styled from 'styled-components'
-import PropTypes from 'prop-types'
+// @flow
 
-import { EndpointField } from '../../../components'
+import React from 'react'
 
-export const Wrapper = styled.div`
-  display: flex;
-  flex-direction: column;
-  min-height: 0;
-`
-export const Fields = styled.div`
-  display: flex;
-  margin-top: 32px;
-  flex-direction: column;
-  overflow: auto;
-`
-export const FieldStyled = styled(EndpointField) `
-  min-width: 224px;
-  max-width: 224px;
-  margin-bottom: 16px;
-`
-export const Row = styled.div`
-  display: flex;
-  flex-shrink: 0;
-  justify-content: space-between;
-`
+import type { Field } from '../../../types/Field'
 
-class ContentPlugin extends React.Component {
-  static propTypes = {
-    connectionInfoSchema: PropTypes.array,
-    validation: PropTypes.object,
-    invalidFields: PropTypes.array,
-    getFieldValue: PropTypes.func,
-    handleFieldChange: PropTypes.func,
-    disabled: PropTypes.bool,
-    cancelButtonText: PropTypes.string,
-    validating: PropTypes.bool,
-    handleValidateClick: PropTypes.func,
-    handleCancelClick: PropTypes.func,
-    onRef: PropTypes.func,
-  }
+import { Wrapper, Fields, FieldStyled, Row } from '../default/ContentPlugin'
 
+type Props = {
+  connectionInfoSchema: Field[],
+  validation: { valid: boolean, validation: { message: string } },
+  invalidFields: string[],
+  getFieldValue: (field: ?Field) => any,
+  handleFieldChange: (field: Field, value: any) => void,
+  disabled: boolean,
+  cancelButtonText: string,
+  validating: boolean,
+  onRef: (contentPlugin: any) => void,
+}
+class ContentPlugin extends React.Component<Props> {
   componentDidMount() {
     this.props.onRef(this)
   }
@@ -64,8 +41,15 @@ class ContentPlugin extends React.Component {
   }
 
   findInvalidFields = () => {
+    const apiVersion = this.props.getFieldValue(this.props.connectionInfoSchema.find(n => n.name === 'identity_api_version'))
     const invalidFields = this.props.connectionInfoSchema.filter(field => {
-      if (field.required) {
+      let required
+      if (typeof field.required === 'function') {
+        required = field.required(apiVersion)
+      } else {
+        required = field.required
+      }
+      if (required) {
         let value = this.props.getFieldValue(field)
         return !value
       }
@@ -78,10 +62,12 @@ class ContentPlugin extends React.Component {
   renderFields() {
     const rows = []
     let lastField
+    let apiVersion = this.props.getFieldValue(this.props.connectionInfoSchema.find(n => n.name === 'identity_api_version'))
     this.props.connectionInfoSchema.forEach((field, i) => {
       const currentField = (
         <FieldStyled
           {...field}
+          required={typeof field.required === 'function' ? field.required(apiVersion) : field.required}
           large
           disabled={this.props.disabled}
           password={field.name === 'password'}

+ 75 - 0
src/plugins/endpoint/openstack/SchemaPlugin.js

@@ -0,0 +1,75 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// @flow
+
+import type { Schema } from '../../../types/Schema'
+import type { Field } from '../../../types/Field'
+
+import DefaultConnectionSchemaParser from '../default/SchemaPlugin'
+
+const customSort = (fields: Field[]) => {
+  const sortPriority = {
+    name: 1,
+    description: 2,
+    username: 3,
+    password: 4,
+    auth_url: 5,
+    project_name: 6,
+    glance_api_version: 7,
+    identity_api_version: 8,
+    project_domain_name: 9,
+    user_domain_name: 10,
+  }
+  fields.sort((a, b) => {
+    if (sortPriority[a.name] && sortPriority[b.name]) {
+      return sortPriority[a.name] - sortPriority[b.name]
+    }
+    if (sortPriority[a.name]) {
+      return -1
+    }
+    if (sortPriority[b.name]) {
+      return 1
+    }
+    return a.name.localeCompare(b.name)
+  })
+
+  return fields
+}
+
+export default class ConnectionSchemaParser {
+  static parseSchemaToFields(schema: Schema): Field[] {
+    let fields = DefaultConnectionSchemaParser.parseSchemaToFields(schema)
+    let identityField = fields.find(f => f.name === 'identity_api_version')
+    if (identityField && !identityField.default) {
+      identityField.default = identityField.minimum
+    }
+    customSort(fields)
+
+    let projectDomainField = fields.find(f => f.name === 'project_domain_name')
+    let userDomainField = fields.find(f => f.name === 'user_domain_name')
+    let requiredFunc = (apiVersion: number) => apiVersion > 2
+    if (projectDomainField && userDomainField) {
+      projectDomainField.required = requiredFunc
+      userDomainField.required = requiredFunc
+    }
+
+    return fields
+  }
+
+  static parseFieldsToPayload(data: { [string]: mixed }, schema: Schema) {
+    let payload = DefaultConnectionSchemaParser.parseFieldsToPayload(data, schema)
+    return payload
+  }
+}

+ 3 - 1
src/types/Field.js

@@ -19,8 +19,10 @@ export type Field = {
   type?: string,
   value?: any,
   enum?: string[],
-  required?: boolean,
+  required?: boolean | (value: any) => boolean,
   default?: any,
   items?: Field[],
   fields?: Field[],
+  minimum?: number,
+  maximum?: number,
 }

+ 25 - 0
src/types/Schema.js

@@ -0,0 +1,25 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// @flow
+
+export type Schema = {
+  properties: {
+    [string]: {
+      name: string,
+    }
+  },
+  required: string[],
+  oneOf: Schema[],
+}