diff --git a/pkg/plugin/driver.go b/pkg/plugin/driver.go index 6275945..68086e7 100644 --- a/pkg/plugin/driver.go +++ b/pkg/plugin/driver.go @@ -5,14 +5,15 @@ import ( "database/sql" "encoding/json" "fmt" - sdkproxy "github.com/grafana/grafana-plugin-sdk-go/backend/proxy" - "github.com/lib/pq" - "golang.org/x/net/proxy" "net" "strconv" "strings" "time" + sdkproxy "github.com/grafana/grafana-plugin-sdk-go/backend/proxy" + "github.com/lib/pq" + "golang.org/x/net/proxy" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/build" @@ -106,14 +107,10 @@ func GenerateConnectionString(settings Settings, version string) (string, error) connStr += fmt.Sprintf(" application_name='%s'", version) } - if len(settings.Timeout) > 0 { - t, err := strconv.Atoi(settings.Timeout) - if err != nil { - return "", errors.New(fmt.Sprintf("invalid timeout: %s", settings.Timeout)) - } - - if t > -1 { - connStr += fmt.Sprintf(" connect_timeout=%d", t) + if settings.Timeout > 0 { + t := strconv.Itoa(int(settings.Timeout)) + if i, err := strconv.Atoi(t); err == nil && i > -1 { + connStr += fmt.Sprintf(" connect_timeout=%d", i) } } @@ -220,7 +217,7 @@ func (h *QuestDB) Settings(config backend.DataSourceInstanceSettings) sqlds.Driv settings, err := LoadSettings(config) timeout := 60 if err == nil { - t, err := strconv.Atoi(settings.QueryTimeout) + t, err := strconv.Atoi(strconv.FormatInt(settings.QueryTimeout, 10)) if err == nil { timeout = t } diff --git a/pkg/plugin/settings.go b/pkg/plugin/settings.go index dfe5edf..5292610 100644 --- a/pkg/plugin/settings.go +++ b/pkg/plugin/settings.go @@ -22,8 +22,8 @@ type Settings struct { TlsClientCert string TlsClientKey string - Timeout string `json:"timeout,omitempty"` - QueryTimeout string `json:"queryTimeout,omitempty"` + Timeout int64 `json:"timeout,omitempty"` + QueryTimeout int64 `json:"queryTimeout,omitempty"` ProxyOptions *proxy.Options MaxOpenConnections int64 `json:"maxOpenConnections,omitempty"` MaxIdleConnections int64 `json:"maxIdleConnections,omitempty"` @@ -83,22 +83,28 @@ func LoadSettings(config backend.DataSourceInstanceSettings) (settings Settings, } if jsonData["timeout"] != nil { - settings.Timeout = jsonData["timeout"].(string) + if val, ok := jsonData["timeout"].(string); ok { + timeout, err := strconv.ParseInt(val, 0, 64) + if err != nil { + return settings, fmt.Errorf("could not parse timeout value: %w", err) + } + settings.Timeout = timeout + } + if val, ok := jsonData["timeout"].(float64); ok { + settings.Timeout = int64(val) + } } if jsonData["queryTimeout"] != nil { - if val, ok := jsonData["queryTimeout"].(string); ok { + if val, ok := jsonData["queryTimeout"].(int64); ok { settings.QueryTimeout = val } if val, ok := jsonData["queryTimeout"].(float64); ok { - settings.QueryTimeout = fmt.Sprintf("%d", int64(val)) + settings.QueryTimeout = int64(val) } } - if strings.TrimSpace(settings.Timeout) == "" { - settings.Timeout = "10" - } - if strings.TrimSpace(settings.QueryTimeout) == "" { - settings.QueryTimeout = "60" + if strings.TrimSpace(strconv.FormatInt(settings.QueryTimeout, 10)) == "" { + settings.QueryTimeout = 60 } password, ok := config.DecryptedSecureJSONData["password"] if ok { @@ -140,7 +146,7 @@ func LoadSettings(config backend.DataSourceInstanceSettings) (settings Settings, if err == nil && proxyOpts != nil { // the sdk expects the timeout to not be a string - timeout, err := strconv.ParseFloat(settings.Timeout, 64) + timeout, err := strconv.ParseFloat(strconv.FormatInt(settings.Timeout, 10), 64) if err == nil { proxyOpts.Timeouts.Timeout = (time.Duration(timeout) * time.Second) } diff --git a/pkg/plugin/settings_test.go b/pkg/plugin/settings_test.go index 9ac52b6..40d346f 100644 --- a/pkg/plugin/settings_test.go +++ b/pkg/plugin/settings_test.go @@ -28,7 +28,7 @@ func TestLoadSettings(t *testing.T) { args: args{ config: backend.DataSourceInstanceSettings{ UID: "ds-uid", - JSONData: []byte(`{ "server": "test", "port": 8812, "username": "john", "timeout": "10", "queryTimeout": "50", + JSONData: []byte(`{ "server": "test", "port": 8812, "username": "john", "timeout": 10, "queryTimeout": 50, "enableSecureSocksProxy": false, "tlsMode": "disable", "maxOpenConnections": 100, "maxIdleConnections": 100, "maxConnectionLifetime": 14400 }`), DecryptedSecureJSONData: map[string]string{"password": "doe"}, @@ -39,8 +39,8 @@ func TestLoadSettings(t *testing.T) { Port: 8812, Username: "john", Password: "doe", - Timeout: "10", - QueryTimeout: "50", + Timeout: 10, + QueryTimeout: 50, MaxOpenConnections: 100, MaxIdleConnections: 100, MaxConnectionLifetime: 14400, @@ -53,7 +53,7 @@ func TestLoadSettings(t *testing.T) { args: args{ config: backend.DataSourceInstanceSettings{ UID: "ds-uid", - JSONData: []byte(`{ "server": "test", "port": 1000, "username": "john", "timeout": "10", "queryTimeout": "50", + JSONData: []byte(`{ "server": "test", "port": 1000, "username": "john", "timeout": 10, "queryTimeout": 50, "enableSecureSocksProxy": true, "tlsMode": "verify-full", "tlsConfigurationMethod": "file-content", "maxOpenConnections": 100, "maxIdleConnections": 100, "maxConnectionLifetime": 14400 }`), DecryptedSecureJSONData: map[string]string{"password": "doe", "tlsCACert": "caCert", "tlsClientCert": "clientCert", "tlsClientKey": "clientKey", "secureSocksProxyPassword": "test"}, @@ -67,8 +67,8 @@ func TestLoadSettings(t *testing.T) { TlsCACert: "caCert", TlsClientCert: "clientCert", TlsClientKey: "clientKey", - Timeout: "10", - QueryTimeout: "50", + Timeout: 10, + QueryTimeout: 50, MaxOpenConnections: 100, MaxIdleConnections: 100, MaxConnectionLifetime: 14400, @@ -96,7 +96,7 @@ func TestLoadSettings(t *testing.T) { JSONData: []byte(`{ "server": "test", "port": 8812, "username": "john", "enableSecureSocksProxy": true, "tlsMode": "verify-ca", "tlsConfigurationMethod": "file-path", "tlsCACertFile": "/var/caCertFile", "tlsClientCertFile": "/var/clientCertFile", "tlsClientKeyFile": "/var/clientKeyFile", - "timeout": "10", "queryTimeout": "50", "maxOpenConnections": 100, "maxIdleConnections": 100, "maxConnectionLifetime": 14400 }`), + "timeout": 10, "queryTimeout": 50, "maxOpenConnections": 100, "maxIdleConnections": 100, "maxConnectionLifetime": 14400 }`), DecryptedSecureJSONData: map[string]string{"password": "rambo", "secureSocksProxyPassword": "test"}, }, }, @@ -108,8 +108,8 @@ func TestLoadSettings(t *testing.T) { TlsCACertFile: "/var/caCertFile", TlsClientCertFile: "/var/clientCertFile", TlsClientKeyFile: "/var/clientKeyFile", - Timeout: "10", - QueryTimeout: "50", + Timeout: 10, + QueryTimeout: 50, MaxOpenConnections: 100, MaxIdleConnections: 100, MaxConnectionLifetime: 14400, @@ -133,7 +133,7 @@ func TestLoadSettings(t *testing.T) { name: "should converting string values to the correct type", args: args{ config: backend.DataSourceInstanceSettings{ - JSONData: []byte(`{"server": "test", "username": "u", "port": "1234", "timeout": "15", "queryTimeout": "25", "maxOpenConnections": 10, "maxIdleConnections": 5, "maxConnectionLifetime": 3600 }`), + JSONData: []byte(`{"server": "test", "username": "u", "port": "1234", "timeout": 15, "queryTimeout": 25, "maxOpenConnections": 10, "maxIdleConnections": 5, "maxConnectionLifetime": 3600 }`), DecryptedSecureJSONData: map[string]string{"password": "p"}, }, }, @@ -142,8 +142,8 @@ func TestLoadSettings(t *testing.T) { Port: 1234, Username: "u", Password: "p", - Timeout: "15", - QueryTimeout: "25", + Timeout: 15, + QueryTimeout: 25, MaxOpenConnections: 10, MaxIdleConnections: 5, MaxConnectionLifetime: 3600, diff --git a/src/__mocks__/datasource.ts b/src/__mocks__/datasource.ts index ddf8c37..a775001 100644 --- a/src/__mocks__/datasource.ts +++ b/src/__mocks__/datasource.ts @@ -1,21 +1,21 @@ import { PluginType } from '@grafana/data'; -import {QuestDBQuery, QueryType} from '../types'; +import { QuestDBQuery, QueryType, Format } from '../types'; import { Datasource } from '../data/QuestDbDatasource'; export const mockDatasource = new Datasource({ id: 1, uid: 'questdb_ds', - type: 'questdb-questdb-datasource', + type: 'grafana-questdb-datasource', name: 'QuestDB', jsonData: { server: 'foo.com', port: 443, - username: 'user' + username: 'user', }, readOnly: true, access: 'direct', meta: { - id: 'questdb-grafana-datasource', + id: 'questdb-questdb-datasource', name: 'QuestDB', type: PluginType.datasource, module: '', @@ -42,5 +42,5 @@ export const mockQuery: QuestDBQuery = { refId: '', format: 1, queryType: QueryType.SQL, - selectedFormat: 4, + selectedFormat: Format.AUTO, }; diff --git a/src/selectors.ts b/src/selectors.ts index 1848562..06c8a9b 100644 --- a/src/selectors.ts +++ b/src/selectors.ts @@ -36,15 +36,18 @@ export const Components = { TLSCACertFile: { label: 'TLS/SSL Root Certificate File', - placeholder: 'If the selected TLS/SSL mode requires a server root certificate, provide the path to the file here.', + placeholder: + 'If the selected TLS/SSL mode requires a server root certificate, provide the path to the file here.', }, TLSClientCertFile: { label: 'TLS/SSL Client Certificate File', - placeholder: 'To authenticate with an TLS/SSL client certificate, provide the path to the file here. Be sure that the file is readable by the user executing the grafana process.', + placeholder: + 'To authenticate with an TLS/SSL client certificate, provide the path to the file here. Be sure that the file is readable by the user executing the grafana process.', }, TLSClientKeyFile: { label: 'TLS/SSL Client Key File', - placeholder: 'To authenticate with a client TLS/SSL certificate, provide the path to the corresponding key file here. Be sure that the file is only readable by the user executing the grafana process.' + placeholder: + 'To authenticate with a client TLS/SSL certificate, provide the path to the corresponding key file here. Be sure that the file is only readable by the user executing the grafana process.', }, Timeout: { @@ -59,18 +62,20 @@ export const Components = { }, TlsMode: { label: 'TLS/SSL Mode', - tooltip: 'This option determines whether or with what priority a secure TLS/SSL TCP/IP connection will be negotiated with the server', - placeholder: "TLS/SSL Mode" + tooltip: + 'This option determines whether or with what priority a secure TLS/SSL TCP/IP connection will be negotiated with the server. For QuestDB Cloud, use "require". For self-hosted QuestDB, use "disable".', + placeholder: 'TLS/SSL Mode', }, TlsMethod: { label: 'TLS/SSL Method', - tooltip: 'This option determines how TLS/SSL certifications are configured. Selecting ' + - '"File system path" will allow you to configure certificates by specifying paths to existing ' + - 'certificates on the local file system where Grafana is running. Be sure that the file is ' + - 'readable by the user executing the Grafana process. ' + - 'Selecting "Certificate content" will allow you to configure certificates by specifying its ' + - 'content. The content will be stored encrypted in Grafana\'s database.', - placeholder: 'TLS/SSL Method' + tooltip: + 'This option determines how TLS/SSL certifications are configured. Selecting ' + + '"File system path" will allow you to configure certificates by specifying paths to existing ' + + 'certificates on the local file system where Grafana is running. Be sure that the file is ' + + 'readable by the user executing the Grafana process. ' + + 'Selecting "Certificate content" will allow you to configure certificates by specifying its ' + + "content. The content will be stored encrypted in Grafana's database.", + placeholder: 'TLS/SSL Method', }, SecureSocksProxy: { label: 'Enable Secure Socks Proxy', @@ -89,7 +94,8 @@ export const Components = { MaxConnectionLifetime: { label: 'Max lifetime', placeholder: '14400', - tooltip: 'The maximum amount of time (in seconds) a connection may be reused. If set to 0, connections are reused forever.', + tooltip: + 'The maximum amount of time (in seconds) a connection may be reused. If set to 0, connections are reused forever.', }, }, QueryEditor: { @@ -199,7 +205,7 @@ export const Components = { }, DESIGNATED_TIMESTAMP: { label: 'Designated timestamp', - tooltip: 'Select table\'s designated timestamp', + tooltip: "Select table's designated timestamp", }, ALIGN_TO: { label: 'Align to', diff --git a/src/types.ts b/src/types.ts index dd72294..9a10c3f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -23,8 +23,8 @@ export interface QuestDBConfig extends DataSourceJsonData { tlsAuthWithCACert?: boolean; secure?: boolean; validate?: boolean; - timeout?: string; - queryTimeout?: string; + timeout?: number; + queryTimeout?: number; enableSecureSocksProxy?: boolean; maxOpenConnections?: number; maxIdleConnections?: number; @@ -92,17 +92,17 @@ export enum BuilderMode { } export enum SampleByFillMode { - None= 'NONE', + None = 'NONE', Null = 'NULL', Prev = 'PREV', - Linear = 'LINEAR' + Linear = 'LINEAR', } export enum SampleByAlignToMode { FirstObservation = 'FIRST OBSERVATION', Calendar = 'CALENDAR', CalendarTimeZone = 'CALENDAR TIME ZONE', - CalendarOffset = 'CALENDAR WITH OFFSET' + CalendarOffset = 'CALENDAR WITH OFFSET', } export const modeRequiresValue = (mode: SampleByAlignToMode): boolean => { @@ -119,7 +119,7 @@ export interface SqlBuilderOptionsList { table?: string; fields?: string[]; filters?: Filter[]; - partitionBy?: string[] + partitionBy?: string[]; orderBy?: OrderBy[]; limit?: string; timeField: string; @@ -227,7 +227,7 @@ export enum FilterOperator { ContainedBy = '<<', ContainedByOrEqual = '<<=', WithInGrafanaTimeRange = 'WITH IN DASHBOARD TIME RANGE', - OutsideGrafanaTimeRange = 'OUTSIDE DASHBOARD TIME RANGE' + OutsideGrafanaTimeRange = 'OUTSIDE DASHBOARD TIME RANGE', } export interface CommonFilterProps { @@ -248,7 +248,15 @@ export interface BooleanFilter extends CommonFilterProps { } export interface StringFilter extends CommonFilterProps { - operator: FilterOperator.Equals | FilterOperator.NotEquals | FilterOperator.Like | FilterOperator.NotLike | FilterOperator.ILike | FilterOperator.NotILike | FilterOperator.Match | FilterOperator.NotMatch; + operator: + | FilterOperator.Equals + | FilterOperator.NotEquals + | FilterOperator.Like + | FilterOperator.NotLike + | FilterOperator.ILike + | FilterOperator.NotILike + | FilterOperator.Match + | FilterOperator.NotMatch; value: string; } @@ -307,7 +315,7 @@ export const defaultBuilderQuery: Omit = { mode: BuilderMode.List, fields: [], limit: '100', - timeField: '' + timeField: '', }, format: Format.TABLE, selectedFormat: Format.AUTO, diff --git a/src/views/QuestDBConfigEditor.test.tsx b/src/views/QuestDBConfigEditor.test.tsx index 5baf42c..43c1880 100644 --- a/src/views/QuestDBConfigEditor.test.tsx +++ b/src/views/QuestDBConfigEditor.test.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import {render, screen} from '@testing-library/react'; -import {ConfigEditor} from './QuestDBConfigEditor'; -import {mockConfigEditorProps} from '../__mocks__/ConfigEditor'; -import {Components} from './../selectors'; +import { render, screen } from '@testing-library/react'; +import { ConfigEditor } from './QuestDBConfigEditor'; +import { mockConfigEditorProps } from '../__mocks__/ConfigEditor'; +import { Components } from './../selectors'; import '@testing-library/jest-dom'; -import {PostgresTLSMethods, PostgresTLSModes} from "../types"; +import { PostgresTLSMethods, PostgresTLSModes } from '../types'; jest.mock('@grafana/runtime', () => { const original = jest.requireActual('@grafana/runtime'); @@ -55,7 +55,9 @@ describe('ConfigEditor', () => { expect(screen.queryByText(PostgresTLSModes.disable)).toBeInTheDocument(); expect(screen.queryByPlaceholderText(Components.ConfigEditor.TlsMethod.placeholder)).not.toBeInTheDocument(); expect(screen.queryByPlaceholderText(Components.ConfigEditor.TLSCACertFile.placeholder)).not.toBeInTheDocument(); - expect(screen.queryByPlaceholderText(Components.ConfigEditor.TLSClientCertFile.placeholder)).not.toBeInTheDocument(); + expect( + screen.queryByPlaceholderText(Components.ConfigEditor.TLSClientCertFile.placeholder) + ).not.toBeInTheDocument(); expect(screen.queryByPlaceholderText(Components.ConfigEditor.TLSClientKeyFile.placeholder)).not.toBeInTheDocument(); expect(screen.queryByPlaceholderText(Components.ConfigEditor.TLSCACert.placeholder)).not.toBeInTheDocument(); expect(screen.queryByPlaceholderText(Components.ConfigEditor.TLSClientCert.placeholder)).not.toBeInTheDocument(); @@ -63,15 +65,17 @@ describe('ConfigEditor', () => { }); it('with tlsMode and filePath tlsMethod', async () => { render( - + ); expect(screen.queryByText(PostgresTLSModes.verifyCA)).toBeInTheDocument(); expect(screen.queryByText(Components.ConfigEditor.TlsMethod.placeholder)).toBeInTheDocument(); @@ -81,15 +85,17 @@ describe('ConfigEditor', () => { it('with verifyFull tlsMode and fileContent tlsMethod', async () => { render( - + ); expect(screen.queryByText(PostgresTLSModes.verifyFull)).toBeInTheDocument(); expect(screen.queryByText(Components.ConfigEditor.TlsMethod.placeholder)).toBeInTheDocument(); @@ -112,8 +118,8 @@ describe('ConfigEditor', () => { });*/ it('with additional properties', async () => { const jsonDataOverrides = { - queryTimeout: '100', - timeout: '100', + queryTimeout: 100, + timeout: 100, enableSecureSocksProxy: true, }; render(); diff --git a/src/views/QuestDBConfigEditor.tsx b/src/views/QuestDBConfigEditor.tsx index ed39371..0d849da 100644 --- a/src/views/QuestDBConfigEditor.tsx +++ b/src/views/QuestDBConfigEditor.tsx @@ -2,16 +2,17 @@ import React from 'react'; import { DataSourcePluginOptionsEditorProps, onUpdateDatasourceJsonDataOption, - onUpdateDatasourceSecureJsonDataOption, SelectableValue, + onUpdateDatasourceSecureJsonDataOption, + SelectableValue, } from '@grafana/data'; -import {Field, Input, SecretInput, Select, Switch} from '@grafana/ui'; -import {CertificationKey} from '../components/ui/CertificationKey'; -import {Components} from './../selectors'; -import {PostgresTLSModes, PostgresTLSMethods, QuestDBConfig, QuestDBSecureConfig} from './../types'; -import {gte} from 'semver'; -import {ConfigSection, DataSourceDescription} from '@grafana/experimental'; -import {config} from '@grafana/runtime'; -import {Divider} from 'components/Divider'; +import { Field, Input, SecretInput, Select, Switch } from '@grafana/ui'; +import { CertificationKey } from '../components/ui/CertificationKey'; +import { Components } from './../selectors'; +import { PostgresTLSModes, PostgresTLSMethods, QuestDBConfig, QuestDBSecureConfig } from './../types'; +import { gte } from 'semver'; +import { ConfigSection, DataSourceDescription } from '@grafana/experimental'; +import { config } from '@grafana/runtime'; +import { Divider } from 'components/Divider'; export interface Props extends DataSourcePluginOptionsEditorProps {} @@ -49,10 +50,7 @@ export const ConfigEditor: React.FC = (props) => { }, }); }; - const onSwitchToggle = ( - key: keyof Pick, - value: boolean - ) => { + const onSwitchToggle = (key: keyof Pick, value: boolean) => { onOptionsChange({ ...options, jsonData: { @@ -98,6 +96,22 @@ export const ConfigEditor: React.FC = (props) => { }); }; + const onUpdateNumberOption = ( + key: keyof Pick< + QuestDBConfig, + 'timeout' | 'queryTimeout' | 'maxConnectionLifetime' | 'maxIdleConnections' | 'maxOpenConnections' + >, + value: string + ) => { + onOptionsChange({ + ...options, + jsonData: { + ...options.jsonData, + [key]: value ? +value : undefined, + }, + }); + }; + const tlsModes: Array> = [ { value: PostgresTLSModes.disable, label: 'disable' }, { value: PostgresTLSModes.require, label: 'require' }, @@ -154,10 +168,7 @@ export const ConfigEditor: React.FC = (props) => { - + = (props) => { onUpdateNumberOption('maxOpenConnections', e.currentTarget.value)} + label={Components.ConfigEditor.MaxOpenConnections.label} + aria-label={Components.ConfigEditor.MaxOpenConnections.label} + placeholder={Components.ConfigEditor.MaxOpenConnections.placeholder} + defaultValue={Components.ConfigEditor.MaxOpenConnections.placeholder} + type="number" /> onUpdateNumberOption('maxIdleConnections', e.currentTarget.value)} + label={Components.ConfigEditor.MaxIdleConnections.label} + aria-label={Components.ConfigEditor.MaxIdleConnections.label} + placeholder={Components.ConfigEditor.MaxIdleConnections.placeholder} + defaultValue={Components.ConfigEditor.MaxIdleConnections.placeholder} + type="number" /> onUpdateNumberOption('maxConnectionLifetime', e.currentTarget.value)} + label={Components.ConfigEditor.MaxConnectionLifetime.label} + aria-label={Components.ConfigEditor.MaxConnectionLifetime.label} + placeholder={Components.ConfigEditor.MaxConnectionLifetime.placeholder} + defaultValue={Components.ConfigEditor.MaxConnectionLifetime.placeholder} + type="number" /> onUpdateNumberOption('timeout', e.currentTarget.value)} + label={Components.ConfigEditor.Timeout.label} + aria-label={Components.ConfigEditor.Timeout.label} + placeholder={Components.ConfigEditor.Timeout.placeholder} + defaultValue={Components.ConfigEditor.Timeout.placeholder} + type="number" /> onUpdateNumberOption('queryTimeout', e.currentTarget.value)} + label={Components.ConfigEditor.QueryTimeout.label} + aria-label={Components.ConfigEditor.QueryTimeout.label} + placeholder={Components.ConfigEditor.QueryTimeout.placeholder} + defaultValue={Components.ConfigEditor.QueryTimeout.placeholder} + type="number" /> {config.featureToggles['secureSocksDSProxyEnabled'] && gte(config.buildInfo.version, '10.0.0') && ( - - onSwitchToggle('enableSecureSocksProxy', e.currentTarget.checked)} - /> - + + onSwitchToggle('enableSecureSocksProxy', e.currentTarget.checked)} + /> + )} - onTlsConfigurationMethodChange(e.value)} - placeholder={Components.ConfigEditor.TlsMethod.placeholder} - width={40} + options={tlsMethods} + value={jsonData.tlsConfigurationMethod || PostgresTLSMethods.filePath} + onChange={(e) => onTlsConfigurationMethodChange(e.value)} + placeholder={Components.ConfigEditor.TlsMethod.placeholder} + width={40} /> - {jsonData.tlsConfigurationMethod === PostgresTLSMethods.fileContent ? ( + {jsonData.tlsConfigurationMethod === PostgresTLSMethods.fileContent ? ( + <> + onCertificateChangeFactory('tlsCACert', e.currentTarget.value)} + placeholder={Components.ConfigEditor.TLSCACert.placeholder} + label={Components.ConfigEditor.TLSCACert.label} + onClick={() => onResetClickFactory('tlsCACert')} + /> + {false && ( <> onCertificateChangeFactory('tlsCACert', e.currentTarget.value)} - placeholder={Components.ConfigEditor.TLSCACert.placeholder} - label={Components.ConfigEditor.TLSCACert.label} - onClick={() => onResetClickFactory('tlsCACert')} + hasCert={!!hasTLSClientCert} + onChange={(e) => onCertificateChangeFactory('tlsClientCert', e.currentTarget.value)} + placeholder={Components.ConfigEditor.TLSClientCert.placeholder} + label={Components.ConfigEditor.TLSClientCert.label} + onClick={() => onResetClickFactory('tlsClientCert')} + /> + onCertificateChangeFactory('tlsClientKey', e.currentTarget.value)} + onClick={() => onResetClickFactory('tlsClientKey')} /> - { false && ( - <> - onCertificateChangeFactory('tlsClientCert', e.currentTarget.value)} - placeholder={Components.ConfigEditor.TLSClientCert.placeholder} - label={Components.ConfigEditor.TLSClientCert.label} - onClick={() => onResetClickFactory('tlsClientCert')} - /> - onCertificateChangeFactory('tlsClientKey', e.currentTarget.value)} - onClick={() => onResetClickFactory('tlsClientKey')} - /> - - )} - ) : ( + )} + + ) : ( + <> + + + + {false && ( <> - + + + + - { false && ( - <> - - - - - - - - )} - )} + )} + + )} ) : null}