ProviderStore.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  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. // @flow
  15. import { observable, action, computed, runInAction } from 'mobx'
  16. import ProviderSource from '../sources/ProviderSource'
  17. import apiCaller from '../utils/ApiCaller'
  18. import configLoader from '../utils/Config'
  19. import { providerTypes } from '../constants'
  20. import { OptionsSchemaPlugin } from '../plugins/endpoint'
  21. import type { OptionValues } from '../types/Endpoint'
  22. import type { Field } from '../types/Field'
  23. import type { Providers } from '../types/Providers'
  24. export const getFieldChangeOptions = (config: {
  25. providerName: ?string,
  26. schema: Field[],
  27. data: any,
  28. field: ?Field,
  29. type: 'source' | 'destination',
  30. }) => {
  31. let { providerName, schema, data, field, type } = config
  32. let providerWithEnvOptions = configLoader.config.extraOptionsApiCalls
  33. .find(p => p.name === providerName && p.types.find(t => t === type))
  34. if (!providerName || !providerWithEnvOptions) {
  35. return null
  36. }
  37. let requiredFields = providerWithEnvOptions.requiredFields
  38. let findFieldInSchema = (name: string) => schema.find(f => f.name === name)
  39. let validFields = requiredFields.filter(fn => {
  40. let schemaField = findFieldInSchema(fn)
  41. if (data) {
  42. if (data[fn] === null) {
  43. return false
  44. }
  45. if (data[fn] === undefined && schemaField && schemaField.default) {
  46. return true
  47. }
  48. return data[fn]
  49. }
  50. return false
  51. })
  52. let isCurrentFieldValid = field ? validFields.find(fn => field ? fn === field.name : false) : true
  53. if (validFields.length !== requiredFields.length || !isCurrentFieldValid) {
  54. return null
  55. }
  56. let envData = {}
  57. validFields.forEach(fn => {
  58. envData[fn] = data ? data[fn] : null
  59. if (envData[fn] == null) {
  60. let schemaField = findFieldInSchema(fn)
  61. if (schemaField && schemaField.default) {
  62. envData[fn] = schemaField.default
  63. }
  64. }
  65. })
  66. return envData
  67. }
  68. class ProviderStore {
  69. @observable connectionInfoSchema: Field[] = []
  70. @observable connectionSchemaLoading: boolean = false
  71. @observable providers: ?Providers
  72. @observable providersLoading: boolean = false
  73. @observable destinationSchema: Field[] = []
  74. @observable destinationSchemaLoading: boolean = false
  75. @observable destinationOptions: OptionValues[] = []
  76. // Set to true while loading the options call for the first set of options
  77. @observable destinationOptionsPrimaryLoading: boolean = false
  78. // Set to true while loading the options call with a set of values in the 'env' parameter
  79. @observable destinationOptionsSecondaryLoading: boolean = false
  80. @observable sourceOptions: OptionValues[] = []
  81. // Set to true while loading the options call for the first set of options
  82. @observable sourceOptionsPrimaryLoading: boolean = false
  83. // Set to true while loading the options call with a set of values in the 'env' parameter
  84. @observable sourceOptionsSecondaryLoading: boolean = false
  85. @observable sourceSchema: Field[] = []
  86. @observable sourceSchemaLoading: boolean = false
  87. lastDestinationSchemaType: 'replica' | 'migration' = 'replica'
  88. @computed
  89. get providerNames(): string[] {
  90. let sortPriority = configLoader.config.providerSortPriority
  91. let array = Object.keys(this.providers || {}).sort((a, b) => {
  92. if (sortPriority[a] && sortPriority[b]) {
  93. return (sortPriority[a] - sortPriority[b]) || a.localeCompare(b)
  94. }
  95. if (sortPriority[a]) {
  96. return -1
  97. }
  98. if (sortPriority[b]) {
  99. return 1
  100. }
  101. return a.localeCompare(b)
  102. })
  103. return array
  104. }
  105. @action async getConnectionInfoSchema(providerName: string): Promise<void> {
  106. this.connectionSchemaLoading = true
  107. try {
  108. let fields: Field[] = await ProviderSource.getConnectionInfoSchema(providerName)
  109. runInAction(() => { this.connectionInfoSchema = fields })
  110. } catch (err) {
  111. throw err
  112. } finally {
  113. runInAction(() => { this.connectionSchemaLoading = false })
  114. }
  115. }
  116. @action clearConnectionInfoSchema() {
  117. this.connectionInfoSchema = []
  118. }
  119. @action async loadProviders(): Promise<void> {
  120. if (this.providers) {
  121. return
  122. }
  123. this.providersLoading = true
  124. try {
  125. let providers: Providers = await ProviderSource.loadProviders()
  126. runInAction(() => { this.providers = providers })
  127. } finally {
  128. runInAction(() => { this.providersLoading = false })
  129. }
  130. }
  131. @action async loadOptionsSchema(options: {
  132. providerName: string,
  133. optionsType: 'source' | 'destination',
  134. useCache?: boolean,
  135. quietError?: boolean,
  136. }): Promise<Field[]> {
  137. let { providerName, optionsType, useCache, quietError } = options
  138. if (optionsType === 'source') {
  139. this.sourceSchemaLoading = true
  140. } else {
  141. this.destinationSchemaLoading = true
  142. }
  143. try {
  144. let fields: Field[] = await ProviderSource.loadOptionsSchema(providerName, optionsType, useCache, quietError)
  145. this.loadOptionsSchemaSuccess(fields, optionsType)
  146. return fields
  147. } catch (err) {
  148. throw err
  149. } finally {
  150. this.loadOptionsSchemaDone(optionsType)
  151. }
  152. }
  153. @action loadOptionsSchemaSuccess(fields: Field[], optionsType: 'source' | 'destination') {
  154. if (optionsType === 'source') {
  155. this.sourceSchema = fields
  156. } else {
  157. this.destinationSchema = fields
  158. }
  159. }
  160. @action loadOptionsSchemaDone(optionsType: 'source' | 'destination') {
  161. if (optionsType === 'source') {
  162. this.sourceSchemaLoading = false
  163. } else {
  164. this.destinationSchemaLoading = false
  165. }
  166. }
  167. async getOptionsValues(config: {
  168. optionsType: 'source' | 'destination',
  169. endpointId: string,
  170. providerName: string,
  171. envData?: ?{ [string]: mixed },
  172. useCache?: boolean,
  173. quietError?: boolean,
  174. }): Promise<OptionValues[]> {
  175. let { providerName, optionsType, endpointId, envData, useCache, quietError } = config
  176. let providerType = optionsType === 'source' ? providerTypes.SOURCE_OPTIONS : providerTypes.DESTINATION_OPTIONS
  177. await this.loadProviders()
  178. if (!this.providers) {
  179. return []
  180. }
  181. let providerWithExtraOptions = this.providers[providerName].types.find(t => t === providerType)
  182. if (!providerWithExtraOptions) {
  183. return []
  184. }
  185. let canceled = false
  186. apiCaller.cancelRequests(endpointId)
  187. this.getOptionsValuesStart(optionsType, !envData)
  188. try {
  189. let options = await ProviderSource.getOptionsValues(optionsType, endpointId, envData, useCache, quietError)
  190. this.getOptionsValuesSuccess(optionsType, providerName, options)
  191. return options
  192. } catch (err) {
  193. console.error(err)
  194. canceled = err ? err.canceled : false
  195. if (canceled) {
  196. return optionsType === 'source' ? [...this.sourceOptions] : [...this.destinationOptions]
  197. }
  198. throw err
  199. } finally {
  200. if (!canceled) {
  201. this.getOptionsValuesDone(optionsType, !envData)
  202. }
  203. }
  204. }
  205. @action getOptionsValuesStart(optionsType: 'source' | 'destination', isPrimary: boolean) {
  206. if (optionsType === 'source') {
  207. if (isPrimary) {
  208. this.sourceOptions = []
  209. this.sourceOptionsPrimaryLoading = true
  210. } else {
  211. this.sourceOptionsSecondaryLoading = true
  212. }
  213. } else if (isPrimary) {
  214. this.destinationOptions = []
  215. this.destinationOptionsPrimaryLoading = true
  216. } else {
  217. this.destinationOptionsSecondaryLoading = true
  218. }
  219. }
  220. @action getOptionsValuesDone(optionsType: 'source' | 'destination', isPrimary: boolean) {
  221. if (optionsType === 'source') {
  222. if (isPrimary) {
  223. this.sourceOptionsPrimaryLoading = false
  224. } else {
  225. this.sourceOptionsSecondaryLoading = false
  226. }
  227. } else if (isPrimary) {
  228. this.destinationOptionsPrimaryLoading = false
  229. } else {
  230. this.destinationOptionsSecondaryLoading = false
  231. }
  232. }
  233. @action getOptionsValuesSuccess(optionsType: 'source' | 'destination', provider: string, options: OptionValues[]) {
  234. let schema = optionsType === 'source' ? this.sourceSchema : this.destinationSchema
  235. schema.forEach(field => {
  236. const parser = OptionsSchemaPlugin[provider] || OptionsSchemaPlugin.default
  237. parser.fillFieldValues(field, options)
  238. })
  239. if (optionsType === 'source') {
  240. this.sourceSchema = [...schema]
  241. this.sourceOptions = options
  242. } else {
  243. this.destinationSchema = [...schema]
  244. this.destinationOptions = options
  245. }
  246. }
  247. }
  248. export default new ProviderStore()