| 1 | <template> |
| 2 | <div class="connector-form"> |
| 3 | <n-spin :show="loading"> |
| 4 | <div class="connector-header"> |
| 5 | <n-avatar |
| 6 | class="connector-image" |
| 7 | object-fit="contain" |
| 8 | round |
| 9 | :size="60" |
| 10 | :src="`/images/connectors/${ |
| 11 | connector ? `${connector.connector_name.toLowerCase()}.svg` : 'default-logo.svg' |
| 12 | }`" |
| 13 | :alt="`${connector.connector_name} Logo`" |
| 14 | fallback-src="/images/img-not-found.svg" |
| 15 | /> |
| 16 | |
| 17 | <h3>{{ connector.connector_name || "" }}</h3> |
| 18 | </div> |
| 19 | |
| 20 | <div class="connector-form-type"> |
| 21 | <CredentialsType |
| 22 | v-if="connectorFormType === ConnectorFormType.CREDENTIALS" |
| 23 | :form="connectorForm" |
| 24 | @mounted="formRef = $event" |
| 25 | /> |
| 26 | <FileType |
| 27 | v-if="connectorFormType === ConnectorFormType.FILE" |
| 28 | :form="connectorForm" |
| 29 | @mounted="formRef = $event" |
| 30 | /> |
| 31 | <TokenType |
| 32 | v-if="connectorFormType === ConnectorFormType.TOKEN" |
| 33 | :form="connectorForm" |
| 34 | @mounted="formRef = $event" |
| 35 | /> |
| 36 | <HostType |
| 37 | v-if="connectorFormType === ConnectorFormType.HOST" |
| 38 | :form="connectorForm" |
| 39 | @mounted="formRef = $event" |
| 40 | /> |
| 41 | </div> |
| 42 | <div class="connector-form-options"> |
| 43 | <n-form |
| 44 | ref="formOptionsRef" |
| 45 | :model="connectorForm" |
| 46 | :rules="optionsRules" |
| 47 | label-width="120px" |
| 48 | label-placement="top" |
| 49 | > |
| 50 | <n-form-item v-if="connectorFormOptions.extraData" label="Extra data" path="connector_extra_data"> |
| 51 | <n-input v-model:value="connectorForm.connector_extra_data" type="text" /> |
| 52 | </n-form-item> |
| 53 | </n-form> |
| 54 | </div> |
| 55 | |
| 56 | <div class="connector-footer mt-4"> |
| 57 | <n-form-item> |
| 58 | <div class="flex w-full justify-end gap-2"> |
| 59 | <n-button type="primary" @click="saveConnector()">Save</n-button> |
| 60 | <n-button @click="closeForm(false)">Cancel</n-button> |
| 61 | </div> |
| 62 | </n-form-item> |
| 63 | </div> |
| 64 | </n-spin> |
| 65 | </div> |
| 66 | </template> |
| 67 | |
| 68 | <script setup lang="ts"> |
| 69 | import type { FormInst, FormRules, FormValidationError } from "naive-ui" |
| 70 | import type { |
| 71 | Connector, |
| 72 | ConnectorForm, |
| 73 | ConnectorFormOptionKeys, |
| 74 | ConnectorFormOptions, |
| 75 | ConnectorRequestPayload |
| 76 | } from "@/types/connectors.d" |
| 77 | import _pick from "lodash/pick" |
| 78 | import { NAvatar, NButton, NForm, NFormItem, NInput, NSpin, useMessage } from "naive-ui" |
| 79 | import { computed, onMounted, ref, toRefs, watch } from "vue" |
| 80 | import Api from "@/api" |
| 81 | import { ConnectorFormType } from "@/types/connectors.d" |
| 82 | import CredentialsType from "./FormTypes/CredentialsType.vue" |
| 83 | import FileType from "./FormTypes/FileType.vue" |
| 84 | import HostType from "./FormTypes/HostType.vue" |
| 85 | import TokenType from "./FormTypes/TokenType.vue" |
| 86 | |
| 87 | const props = defineProps<{ |
| 88 | connector: Connector |
| 89 | }>() |
| 90 | const emit = defineEmits<{ |
| 91 | (e: "close", value: boolean): void |
| 92 | (e: "loading", value: boolean): void |
| 93 | }>() |
| 94 | |
| 95 | const { connector } = toRefs(props) |
| 96 | |
| 97 | const connectorForm = ref<ConnectorForm>({ |
| 98 | connector_url: "", |
| 99 | connector_username: "", |
| 100 | connector_password: "", |
| 101 | connector_api_key: "", |
| 102 | connector_extra_data: "", |
| 103 | connector_file: null |
| 104 | }) |
| 105 | |
| 106 | const optionsRules: FormRules = { |
| 107 | connector_extra_data: [{ required: true, trigger: "blur", message: "Please input a valid Extra Data" }] |
| 108 | } |
| 109 | |
| 110 | const message = useMessage() |
| 111 | const formOptionsRef = ref<FormInst>() |
| 112 | const connectorFormType = computed<ConnectorFormType>(() => getConnectorFormType(connector.value)) |
| 113 | const connectorFormOptions = computed<ConnectorFormOptions>(() => getConnectorFormOptions(connector.value)) |
| 114 | const isConnectorConfigured = computed<boolean>(() => connector.value.connector_configured) |
| 115 | const formRef = ref<FormInst | null>(null) |
| 116 | const loading = ref<boolean>(false) |
| 117 | |
| 118 | const formOptionsCheckRequired = computed<boolean>(() => { |
| 119 | let checkRequired = false |
| 120 | for (const key in connectorFormOptions.value) { |
| 121 | const required = connectorFormOptions.value[key as ConnectorFormOptionKeys] |
| 122 | if (required === true) { |
| 123 | checkRequired = true |
| 124 | } |
| 125 | } |
| 126 | return checkRequired |
| 127 | }) |
| 128 | |
| 129 | watch(loading, val => { |
| 130 | emit("loading", val) |
| 131 | }) |
| 132 | |
| 133 | function setUpForm() { |
| 134 | connectorForm.value = _pick(connector.value, [ |
| 135 | "connector_url", |
| 136 | "connector_username", |
| 137 | "connector_password", |
| 138 | "connector_api_key", |
| 139 | "connector_extra_data", |
| 140 | "connector_file" |
| 141 | ]) as unknown as ConnectorForm |
| 142 | } |
| 143 | |
| 144 | function getConnectorFormType(connector: Connector): ConnectorFormType { |
| 145 | if (connector.connector_accepts_api_key) { |
| 146 | return ConnectorFormType.TOKEN |
| 147 | } |
| 148 | if (connector.connector_accepts_file) { |
| 149 | return ConnectorFormType.FILE |
| 150 | } |
| 151 | if (connector.connector_accepts_username_password) { |
| 152 | return ConnectorFormType.CREDENTIALS |
| 153 | } |
| 154 | if (connector.connector_accepts_host_only) { |
| 155 | return ConnectorFormType.HOST |
| 156 | } |
| 157 | return ConnectorFormType.UNKNOWN |
| 158 | } |
| 159 | |
| 160 | function getConnectorFormOptions(connector: Connector): ConnectorFormOptions { |
| 161 | const options: ConnectorFormOptions = {} |
| 162 | |
| 163 | if (connector.connector_accepts_extra_data) { |
| 164 | options.extraData = true |
| 165 | } |
| 166 | |
| 167 | return options |
| 168 | } |
| 169 | |
| 170 | function saveConnector() { |
| 171 | if (!formRef.value) return |
| 172 | |
| 173 | let messageSent = false |
| 174 | |
| 175 | formRef.value.validate((errors?: Array<FormValidationError>) => { |
| 176 | if (!errors) { |
| 177 | if (formOptionsRef.value && formOptionsCheckRequired.value) { |
| 178 | formOptionsRef.value.validate((errors?: Array<FormValidationError>) => { |
| 179 | if (!errors) { |
| 180 | configureConnector() |
| 181 | } else { |
| 182 | if (!messageSent) { |
| 183 | message.warning("You must fill in the required fields correctly.") |
| 184 | } |
| 185 | return false |
| 186 | } |
| 187 | }) |
| 188 | } else { |
| 189 | configureConnector() |
| 190 | } |
| 191 | } else { |
| 192 | message.warning("You must fill in the required fields correctly.") |
| 193 | messageSent = true |
| 194 | return false |
| 195 | } |
| 196 | }) |
| 197 | } |
| 198 | |
| 199 | function closeForm(update: boolean) { |
| 200 | emit("close", update) |
| 201 | } |
| 202 | |
| 203 | function getRequestMethod() { |
| 204 | if (connectorFormType.value === ConnectorFormType.FILE) { |
| 205 | return Api.connectors.upload |
| 206 | } |
| 207 | |
| 208 | return isConnectorConfigured.value ? Api.connectors.update : Api.connectors.configure |
| 209 | } |
| 210 | |
| 211 | function configureConnector() { |
| 212 | loading.value = true |
| 213 | |
| 214 | const { |
| 215 | connector_url, |
| 216 | connector_username, |
| 217 | connector_password, |
| 218 | connector_api_key, |
| 219 | connector_file, |
| 220 | connector_extra_data |
| 221 | } = connectorForm.value |
| 222 | |
| 223 | const requestMethod = getRequestMethod() |
| 224 | |
| 225 | let requestPayload: ConnectorRequestPayload = {} |
| 226 | |
| 227 | if (connectorFormType.value === ConnectorFormType.HOST) { |
| 228 | requestPayload = { |
| 229 | connector_url |
| 230 | } |
| 231 | } |
| 232 | if (connectorFormType.value === ConnectorFormType.TOKEN) { |
| 233 | requestPayload = { |
| 234 | connector_url, |
| 235 | connector_api_key |
| 236 | } |
| 237 | } |
| 238 | if (connectorFormType.value === ConnectorFormType.CREDENTIALS) { |
| 239 | requestPayload = { |
| 240 | connector_url, |
| 241 | connector_username, |
| 242 | connector_password |
| 243 | } |
| 244 | } |
| 245 | if (connectorFormType.value === ConnectorFormType.FILE && connector_file) { |
| 246 | const form = new FormData() |
| 247 | form.append("file", new Blob([connector_file], { type: connector_file.type }), connector_file.name) |
| 248 | requestPayload = form |
| 249 | } |
| 250 | if (connectorFormOptions.value.extraData) { |
| 251 | if (requestPayload instanceof FormData) { |
| 252 | requestPayload.append("connector_extra_data", connector_extra_data) |
| 253 | } else { |
| 254 | requestPayload.connector_extra_data = connector_extra_data |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | requestMethod(connector.value.id, requestPayload) |
| 259 | .then(() => { |
| 260 | message.success("Connector has been successfully configured.") |
| 261 | closeForm(true) |
| 262 | }) |
| 263 | .catch(err => { |
| 264 | if (err.response.status === 400) { |
| 265 | if (isConnectorConfigured.value) { |
| 266 | message.error( |
| 267 | "This connector is not configured. If you would like to configure this connector select `Configure`." |
| 268 | ) |
| 269 | } else { |
| 270 | message.error( |
| 271 | "This connector is already configured. If you would like to reconfigure this connector select `Edit`." |
| 272 | ) |
| 273 | } |
| 274 | } else if (err.response?.status === 401) { |
| 275 | message.error("Unauthorized. Please check all fields") |
| 276 | } else { |
| 277 | message.error( |
| 278 | "Error updating the connector. Your settings were not inserted into the keystore. Please try again." |
| 279 | ) |
| 280 | } |
| 281 | closeForm(false) |
| 282 | }) |
| 283 | .finally(() => { |
| 284 | loading.value = false |
| 285 | }) |
| 286 | } |
| 287 | |
| 288 | onMounted(() => { |
| 289 | setUpForm() |
| 290 | }) |
| 291 | </script> |
| 292 | |
| 293 | <style lang="scss" scoped> |
| 294 | .connector-form { |
| 295 | .connector-header { |
| 296 | display: flex; |
| 297 | align-items: center; |
| 298 | margin-bottom: calc(var(--spacing) * 7); |
| 299 | gap: calc(var(--spacing) * 5); |
| 300 | |
| 301 | .connector-image { |
| 302 | border: 2px solid var(--bg-body-color); |
| 303 | } |
| 304 | } |
| 305 | } |
| 306 | </style> |