ProviderStore.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  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. lastSourceSchemaType: 'replica' | 'migration' = 'replica'
  89. @computed
  90. get providerNames(): string[] {
  91. let sortPriority = configLoader.config.providerSortPriority
  92. let array = Object.keys(this.providers || {}).sort((a, b) => {
  93. if (sortPriority[a] && sortPriority[b]) {
  94. return (sortPriority[a] - sortPriority[b]) || a.localeCompare(b)
  95. }
  96. if (sortPriority[a]) {
  97. return -1
  98. }
  99. if (sortPriority[b]) {
  100. return 1
  101. }
  102. return a.localeCompare(b)
  103. })
  104. return array
  105. }
  106. @action async getConnectionInfoSchema(providerName: string): Promise<void> {
  107. this.connectionSchemaLoading = true
  108. try {
  109. let fields: Field[] = await ProviderSource.getConnectionInfoSchema(providerName)
  110. runInAction(() => { this.connectionInfoSchema = fields })
  111. } catch (err) {
  112. throw err
  113. } finally {
  114. runInAction(() => { this.connectionSchemaLoading = false })
  115. }
  116. }
  117. @action clearConnectionInfoSchema() {
  118. this.connectionInfoSchema = []
  119. }
  120. @action async loadProviders(): Promise<void> {
  121. if (this.providers) {
  122. return
  123. }
  124. this.providersLoading = true
  125. try {
  126. let providers: Providers = await ProviderSource.loadProviders()
  127. runInAction(() => { this.providers = providers })
  128. } finally {
  129. runInAction(() => { this.providersLoading = false })
  130. }
  131. }
  132. @action async loadOptionsSchema(options: {
  133. providerName: string,
  134. schemaType: 'migration' | 'replica',
  135. optionsType: 'source' | 'destination',
  136. useCache?: boolean,
  137. quietError?: boolean,
  138. }): Promise<void> {
  139. let { schemaType, providerName, optionsType, useCache, quietError } = options
  140. if (optionsType === 'source') {
  141. this.lastSourceSchemaType = schemaType
  142. } else {
  143. this.lastDestinationSchemaType = schemaType
  144. }
  145. if (optionsType === 'source') {
  146. this.sourceSchemaLoading = true
  147. } else {
  148. this.destinationSchemaLoading = true
  149. }
  150. try {
  151. let fields: Field[] = await ProviderSource.loadOptionsSchema(providerName, schemaType, optionsType, useCache, quietError)
  152. this.loadOptionsSchemaSuccess(fields, optionsType)
  153. } catch (err) {
  154. throw err
  155. } finally {
  156. this.loadOptionsSchemaDone(optionsType)
  157. }
  158. }
  159. @action loadOptionsSchemaSuccess(fields: Field[], optionsType: 'source' | 'destination') {
  160. if (optionsType === 'source') {
  161. this.sourceSchema = fields
  162. } else {
  163. this.destinationSchema = fields
  164. }
  165. }
  166. @action loadOptionsSchemaDone(optionsType: 'source' | 'destination') {
  167. if (optionsType === 'source') {
  168. this.sourceSchemaLoading = false
  169. } else {
  170. this.destinationSchemaLoading = false
  171. }
  172. }
  173. async getOptionsValues(config: {
  174. optionsType: 'source' | 'destination',
  175. endpointId: string,
  176. providerName: string,
  177. envData?: { [string]: mixed },
  178. useCache?: boolean,
  179. quietError?: boolean,
  180. }): Promise<OptionValues[]> {
  181. let { providerName, optionsType, endpointId, envData, useCache, quietError } = config
  182. let providerType = optionsType === 'source' ? providerTypes.SOURCE_OPTIONS : providerTypes.DESTINATION_OPTIONS
  183. await this.loadProviders()
  184. if (!this.providers) {
  185. return []
  186. }
  187. let providerWithExtraOptions = this.providers[providerName].types.find(t => t === providerType)
  188. if (!providerWithExtraOptions) {
  189. return []
  190. }
  191. let canceled = false
  192. apiCaller.cancelRequests(endpointId)
  193. this.getOptionsValuesStart(optionsType, !envData)
  194. try {
  195. let options = await ProviderSource.getOptionsValues(optionsType, endpointId, envData, useCache, quietError)
  196. this.getOptionsValuesSuccess(optionsType, providerName, options)
  197. return options
  198. } catch (err) {
  199. console.error(err)
  200. canceled = err ? err.canceled : false
  201. if (canceled) {
  202. return optionsType === 'source' ? [...this.sourceOptions] : [...this.destinationOptions]
  203. }
  204. let schemaType = optionsType === 'source' ? this.lastSourceSchemaType : this.lastDestinationSchemaType
  205. if (!envData) {
  206. return []
  207. }
  208. let newOptions = await this.loadOptionsSchema({ providerName, schemaType, optionsType })
  209. return newOptions
  210. } finally {
  211. if (!canceled) {
  212. this.getOptionsValuesDone(optionsType, !envData)
  213. }
  214. }
  215. }
  216. @action getOptionsValuesStart(optionsType: 'source' | 'destination', isPrimary: boolean) {
  217. if (optionsType === 'source') {
  218. if (isPrimary) {
  219. this.sourceOptions = []
  220. this.sourceOptionsPrimaryLoading = true
  221. } else {
  222. this.sourceOptionsSecondaryLoading = true
  223. }
  224. } else if (isPrimary) {
  225. this.destinationOptions = []
  226. this.destinationOptionsPrimaryLoading = true
  227. } else {
  228. this.destinationOptionsSecondaryLoading = true
  229. }
  230. }
  231. @action getOptionsValuesDone(optionsType: 'source' | 'destination', isPrimary: boolean) {
  232. if (optionsType === 'source') {
  233. if (isPrimary) {
  234. this.sourceOptionsPrimaryLoading = false
  235. } else {
  236. this.sourceOptionsSecondaryLoading = false
  237. }
  238. } else if (isPrimary) {
  239. this.destinationOptionsPrimaryLoading = false
  240. } else {
  241. this.destinationOptionsSecondaryLoading = false
  242. }
  243. }
  244. @action getOptionsValuesSuccess(optionsType: 'source' | 'destination', provider: string, options: OptionValues[]) {
  245. let schema = optionsType === 'source' ? this.sourceSchema : this.destinationSchema
  246. schema.forEach(field => {
  247. const parser = OptionsSchemaPlugin[provider] || OptionsSchemaPlugin.default
  248. parser.fillFieldValues(field, options)
  249. })
  250. if (optionsType === 'source') {
  251. this.sourceSchema = [...schema]
  252. this.sourceOptions = options
  253. } else {
  254. this.destinationSchema = [...schema]
  255. this.destinationOptions = options
  256. }
  257. }
  258. }
  259. export default new ProviderStore()