ContentPlugin.jsx 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*
  2. Copyright (C) 2017 Cloudbase Solutions SRL
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. import React from 'react'
  15. import styled from 'styled-components'
  16. import PropTypes from 'prop-types'
  17. import { TextArea } from 'components'
  18. import Palette from '../../../components/styleUtils/Palette'
  19. import StyleProps from '../../../components/styleUtils/StyleProps'
  20. import { Wrapper, Fields, FieldStyled, Row } from '../default/ContentPlugin'
  21. const RadioGroup = styled.div`
  22. width: 100%;
  23. `
  24. const PasteWrapper = styled.div``
  25. const PasteLabel = styled.div`
  26. display: flex;
  27. font-size: 10px;
  28. `
  29. const PasteLabelText = styled.div`
  30. font-weight: ${StyleProps.fontWeights.medium};
  31. color: ${Palette.grayscale[3]};
  32. text-transform: uppercase;
  33. margin-bottom: 4px;
  34. `
  35. const PasteLabelShowMore = styled.div`
  36. color: ${Palette.primary};
  37. margin-left: 5px;
  38. cursor: pointer;
  39. `
  40. const fieldNameMapper = {
  41. activeDirectory: 'active_directory_url',
  42. activeDirectoryDataLakeResourceId: 'active_directory_data_lake_resource_id',
  43. activeDirectoryGraphResourceId: 'active_directory_graph_resource_id',
  44. activeDirectoryResourceId: 'active_directory_resource_id',
  45. batchResourceId: 'batch_resource_endpoint',
  46. gallery: 'gallery_endpoint',
  47. management: 'management_endpoint',
  48. resourceManager: 'resource_manager_endpoint',
  49. sqlManagement: 'sql_management_endpoint',
  50. vmImageAliasDoc: 'vm_image_alias_doc',
  51. azureDatalakeAnalyticsCatalogAndJobEndpoint: 'azure_datalake_analytics_catalog_and_job_endpoint',
  52. azureDatalakeStoreFileSystemEndpoint: 'azure_datalake_store_file_system_endpoint',
  53. keyvaultDns: 'keyvault_dns',
  54. sqlServerHostname: 'sql_server_hostname',
  55. storageEndpoint: 'storage_endpoint',
  56. }
  57. class ContentPlugin extends React.Component {
  58. static propTypes = {
  59. connectionInfoSchema: PropTypes.array,
  60. validation: PropTypes.object,
  61. invalidFields: PropTypes.array,
  62. getFieldValue: PropTypes.func,
  63. handleFieldChange: PropTypes.func,
  64. handleFieldsChange: PropTypes.func,
  65. disabled: PropTypes.bool,
  66. cancelButtonText: PropTypes.string,
  67. validating: PropTypes.bool,
  68. handleValidateClick: PropTypes.func,
  69. handleCancelClick: PropTypes.func,
  70. highlightRequired: PropTypes.func,
  71. onRef: PropTypes.func,
  72. onResizeUpdate: PropTypes.func,
  73. scrollableRef: PropTypes.func,
  74. }
  75. constructor() {
  76. super()
  77. this.state = {
  78. jsonConfig: '',
  79. showPasteInput: false,
  80. }
  81. }
  82. componentDidMount() {
  83. this.props.onRef(this)
  84. }
  85. componentDidUpdate(prevProps, prevState) {
  86. if (this.cloudProfileChanged || prevState.showPasteInput !== this.state.showPasteInput) {
  87. this.props.onResizeUpdate(this.fieldsRef)
  88. this.cloudProfileChanged = false
  89. }
  90. }
  91. componentWillUnmount() {
  92. this.props.onRef(undefined)
  93. }
  94. findInvalidFields = () => {
  95. let invalidFields = []
  96. const find = fields => {
  97. fields.forEach(field => {
  98. if (field.required) {
  99. let value = this.props.getFieldValue(field)
  100. if (!value) {
  101. invalidFields.push(field.name)
  102. }
  103. }
  104. })
  105. }
  106. find(this.props.connectionInfoSchema)
  107. let loginTypeField = this.props.connectionInfoSchema.find(f => f.name === 'login_type')
  108. let selectedLoginTypeField = loginTypeField.items.find(f => f.name === this.props.getFieldValue(loginTypeField))
  109. find(selectedLoginTypeField.fields)
  110. if (this.props.getFieldValue({ name: 'cloud_profile' }) === 'CustomCloud') {
  111. let customCloudFields = this.props.connectionInfoSchema.find(f => f.name === 'cloud_profile').custom_cloud_fields
  112. find(customCloudFields)
  113. }
  114. return invalidFields
  115. }
  116. handleJsonConfigBlur() {
  117. if (this.lastBlurValue && this.lastBlurValue === this.state.jsonConfig) {
  118. return
  119. }
  120. this.lastBlurValue = this.state.jsonConfig
  121. let json
  122. try {
  123. json = JSON.parse(this.state.jsonConfig)
  124. } catch (e) {
  125. return
  126. }
  127. if (!json.endpoints || !json.suffixes) {
  128. return
  129. }
  130. let managementUrl = json.endpoints.management
  131. if (managementUrl && !json.endpoints.sqlManagement) {
  132. if (managementUrl.lastIndexOf('/') === managementUrl.length - 1) {
  133. managementUrl = managementUrl.substr(0, managementUrl.length - 1)
  134. }
  135. json.endpoints.sqlManagement = `${managementUrl}:8443/`
  136. }
  137. let updatedFields = []
  138. const setValue = (object, key) => {
  139. if (object[key]) {
  140. updatedFields.push({ field: { name: fieldNameMapper[key] }, value: object[key] })
  141. }
  142. }
  143. Object.keys(json.endpoints).forEach(k => {
  144. setValue(json.endpoints, k)
  145. })
  146. Object.keys(json.suffixes).forEach(k => {
  147. setValue(json.suffixes, k)
  148. })
  149. this.props.handleFieldsChange(updatedFields)
  150. }
  151. handleFieldChange(field, value) {
  152. if (field.name === 'cloud_profile') {
  153. this.cloudProfileChanged = true
  154. }
  155. this.props.handleFieldChange(field, value)
  156. }
  157. renderField(field, customProps) {
  158. return (
  159. <FieldStyled
  160. {...field}
  161. large
  162. disabled={this.props.disabled}
  163. key={field.name}
  164. password={field.name === 'password'}
  165. highlight={this.props.invalidFields.findIndex(fn => fn === field.name) > -1}
  166. value={this.props.getFieldValue(field)}
  167. onChange={value => { this.handleFieldChange(field, value) }}
  168. {...customProps}
  169. />
  170. )
  171. }
  172. renderFieldRows(fields) {
  173. const rows = []
  174. let lastField
  175. fields.forEach((field, i) => {
  176. const currentField = this.renderField(field)
  177. if (i % 2 !== 0) {
  178. rows.push((
  179. <Row key={field.name}>
  180. {lastField}
  181. {currentField}
  182. </Row>
  183. ))
  184. } else if (i === fields.length - 1) {
  185. rows.push((
  186. <Row key={field.name}>
  187. {currentField}
  188. </Row>
  189. ))
  190. }
  191. lastField = currentField
  192. })
  193. return rows
  194. }
  195. renderPasteField() {
  196. const textArea = (
  197. <TextArea
  198. width="100%"
  199. height="96px"
  200. placeholder="Use the Azure CLI to get the details of a registered cloud and paste it here"
  201. value={this.state.jsonConfig}
  202. onBlur={() => { this.handleJsonConfigBlur() }}
  203. onChange={e => { this.setState({ jsonConfig: e.target.value }) }}
  204. disabled={this.props.disabled}
  205. />
  206. )
  207. return (
  208. <PasteWrapper>
  209. <PasteLabel>
  210. <PasteLabelText>Paste Configuration (optional)</PasteLabelText>
  211. <PasteLabelShowMore
  212. onClick={() => { this.setState({ showPasteInput: !this.state.showPasteInput }) }}
  213. >{this.state.showPasteInput ? 'Hide' : 'Show'}</PasteLabelShowMore>
  214. </PasteLabel>
  215. {this.state.showPasteInput ? textArea : null}
  216. </PasteWrapper>
  217. )
  218. }
  219. renderFields() {
  220. const fields = this.props.connectionInfoSchema
  221. const cloudProfileField = fields.find(f => f.name === 'cloud_profile')
  222. const loginTypeField = fields.find(f => f.name === 'login_type')
  223. const allowUntrustedField = loginTypeField.items.find(f => f.name === this.props.getFieldValue(loginTypeField)).fields.find(f => f.name === 'allow_untrusted')
  224. let renderedFields = this.renderFieldRows(fields.filter(f => f.name !== loginTypeField.name && f.name !== cloudProfileField.name))
  225. const radioGroupRow = (
  226. <Row key="radio-group-row">
  227. <RadioGroup key="radio-group">
  228. {loginTypeField.items.map(field =>
  229. this.renderField(field, {
  230. value: this.props.getFieldValue(loginTypeField) === field.name,
  231. onChange: value => { if (value) this.props.handleFieldChange(loginTypeField, field.name) },
  232. })
  233. )}
  234. </RadioGroup>
  235. {this.renderField(allowUntrustedField)}
  236. </Row>
  237. )
  238. renderedFields.push(radioGroupRow)
  239. renderedFields = renderedFields.concat(this.renderFieldRows(
  240. loginTypeField.items.find(f => f.name === this.props.getFieldValue(loginTypeField)).fields
  241. .filter(f => f.name !== allowUntrustedField.name)
  242. .concat([cloudProfileField])
  243. ))
  244. const isCustomCloud = this.props.getFieldValue(cloudProfileField) === 'CustomCloud'
  245. if (isCustomCloud) {
  246. renderedFields = renderedFields.concat(this.renderFieldRows(cloudProfileField.custom_cloud_fields))
  247. }
  248. return (
  249. <Fields innerRef={ref => { this.props.scrollableRef(ref) }}>
  250. {renderedFields}
  251. {isCustomCloud ? this.renderPasteField() : null}
  252. </Fields>
  253. )
  254. }
  255. render() {
  256. const fields = this.props.connectionInfoSchema
  257. if (fields.length === 0) {
  258. return null
  259. }
  260. return (
  261. <Wrapper>
  262. {this.renderFields()}
  263. </Wrapper>
  264. )
  265. }
  266. }
  267. export default ContentPlugin