in superset-frontend/src/features/databases/DatabaseModal/index.tsx [251:554]
export function dbReducer(
state: Partial<DatabaseObject> | null,
action: DBReducerActionType,
): Partial<DatabaseObject> | null {
const trimmedState = {
...(state || {}),
};
let query = {};
let query_input = '';
let parametersCatalog;
let actionPayloadJson;
const extraJson: ExtraJson = JSON.parse(trimmedState.extra || '{}');
switch (action.type) {
case ActionType.ExtraEditorChange:
// "extra" payload in state is a string
try {
// we don't want to stringify encoded strings twice
actionPayloadJson = JSON.parse(action.payload.json || '{}');
} catch (e) {
actionPayloadJson = action.payload.json;
}
return {
...trimmedState,
extra: JSON.stringify({
...extraJson,
[action.payload.name]: actionPayloadJson,
}),
};
case ActionType.EncryptedExtraInputChange:
return {
...trimmedState,
masked_encrypted_extra: JSON.stringify({
...JSON.parse(trimmedState.masked_encrypted_extra || '{}'),
[action.payload.name]: action.payload.value,
}),
};
case ActionType.ExtraInputChange:
// "extra" payload in state is a string
if (
action.payload.name === 'schema_cache_timeout' ||
action.payload.name === 'table_cache_timeout'
) {
return {
...trimmedState,
extra: JSON.stringify({
...extraJson,
metadata_cache_timeout: {
...extraJson?.metadata_cache_timeout,
[action.payload.name]: action.payload.value,
},
}),
};
}
if (action.payload.name === 'schemas_allowed_for_file_upload') {
return {
...trimmedState,
extra: JSON.stringify({
...extraJson,
schemas_allowed_for_file_upload: (action.payload.value || '')
.split(',')
.filter(schema => schema !== ''),
}),
};
}
if (action.payload.name === 'http_path') {
return {
...trimmedState,
extra: JSON.stringify({
...extraJson,
engine_params: {
connect_args: {
[action.payload.name]: action.payload.value?.trim(),
},
},
}),
};
}
if (action.payload.name === 'expand_rows') {
return {
...trimmedState,
extra: JSON.stringify({
...extraJson,
schema_options: {
...extraJson?.schema_options,
[action.payload.name]: !!action.payload.value,
},
}),
};
}
return {
...trimmedState,
extra: JSON.stringify({
...extraJson,
[action.payload.name]:
action.payload.type === 'checkbox'
? action.payload.checked
: action.payload.value,
}),
};
case ActionType.InputChange:
if (action.payload.type === 'checkbox') {
return {
...trimmedState,
[action.payload.name]: action.payload.checked,
};
}
return {
...trimmedState,
[action.payload.name]: action.payload.value,
};
case ActionType.ParametersChange:
// catalog params will always have a catalog state for
// dbs that use a catalog, i.e., gsheets, even if the
// fields are empty strings
if (
action.payload.type?.startsWith('catalog') &&
trimmedState.catalog !== undefined
) {
// Formatting wrapping google sheets table catalog
const catalogCopy: CatalogObject[] = [...trimmedState.catalog];
const idx = action.payload.type?.split('-')[1];
const catalogToUpdate: CatalogObject =
catalogCopy[parseInt(idx, 10)] || {};
if (action.payload.value !== undefined) {
catalogToUpdate[action.payload.name as keyof CatalogObject] =
action.payload.value;
}
// insert updated catalog to existing state
catalogCopy.splice(parseInt(idx, 10), 1, catalogToUpdate);
// format catalog for state
// eslint-disable-next-line array-callback-return
parametersCatalog = catalogCopy.reduce<Record<string, string>>(
(obj, item: CatalogObject) => {
const catalog = { ...obj };
catalog[item.name as keyof CatalogObject] = item.value;
return catalog;
},
{},
);
return {
...trimmedState,
catalog: catalogCopy,
parameters: {
...trimmedState.parameters,
catalog: parametersCatalog,
},
};
}
return {
...trimmedState,
parameters: {
...trimmedState.parameters,
[action.payload.name]: action.payload.value,
},
};
case ActionType.ParametersSSHTunnelChange:
return {
...trimmedState,
ssh_tunnel: {
...trimmedState.ssh_tunnel,
[action.payload.name]: action.payload.value,
},
};
case ActionType.SetSSHTunnelLoginMethod: {
let ssh_tunnel = {};
if (trimmedState?.ssh_tunnel) {
// remove any attributes that are considered sensitive
ssh_tunnel = pick(trimmedState.ssh_tunnel, [
'id',
'server_address',
'server_port',
'username',
]);
}
if (action.payload.login_method === AuthType.PrivateKey) {
return {
...trimmedState,
ssh_tunnel: {
private_key: trimmedState?.ssh_tunnel?.private_key,
private_key_password:
trimmedState?.ssh_tunnel?.private_key_password,
...ssh_tunnel,
},
};
}
if (action.payload.login_method === AuthType.Password) {
return {
...trimmedState,
ssh_tunnel: {
password: trimmedState?.ssh_tunnel?.password,
...ssh_tunnel,
},
};
}
return {
...trimmedState,
};
}
case ActionType.RemoveSSHTunnelConfig:
return {
...trimmedState,
ssh_tunnel: undefined,
};
case ActionType.AddTableCatalogSheet:
if (trimmedState.catalog !== undefined) {
return {
...trimmedState,
catalog: [...trimmedState.catalog, { name: '', value: '' }],
};
}
return {
...trimmedState,
catalog: [{ name: '', value: '' }],
};
case ActionType.RemoveTableCatalogSheet:
trimmedState.catalog?.splice(action.payload.indexToDelete, 1);
return {
...trimmedState,
};
case ActionType.EditorChange:
return {
...trimmedState,
[action.payload.name]: action.payload.json,
};
case ActionType.QueryChange:
return {
...trimmedState,
parameters: {
...trimmedState.parameters,
query: Object.fromEntries(new URLSearchParams(action.payload.value)),
},
query_input: action.payload.value,
};
case ActionType.TextChange:
return {
...trimmedState,
[action.payload.name]: action.payload.value,
};
case ActionType.Fetched:
// convert query to a string and store in query_input
query = action.payload?.parameters?.query || {};
query_input = Object.entries(query)
.map(([key, value]) => `${key}=${value}`)
.join('&');
if (
action.payload.masked_encrypted_extra &&
action.payload.configuration_method === ConfigurationMethod.DynamicForm
) {
// "extra" payload from the api is a string
const extraJsonPayload: ExtraJson = {
...JSON.parse((action.payload.extra as string) || '{}'),
};
const payloadCatalog = extraJsonPayload.engine_params?.catalog;
const engineRootCatalog = Object.entries(payloadCatalog || {}).map(
([name, value]: string[]) => ({ name, value }),
);
return {
...action.payload,
engine: action.payload.backend || trimmedState.engine,
configuration_method: action.payload.configuration_method,
catalog: engineRootCatalog,
parameters: {
...(action.payload.parameters || trimmedState.parameters),
catalog: payloadCatalog,
},
query_input,
};
}
return {
...action.payload,
masked_encrypted_extra: action.payload.masked_encrypted_extra || '',
engine: action.payload.backend || trimmedState.engine,
configuration_method: action.payload.configuration_method,
parameters: action.payload.parameters || trimmedState.parameters,
ssh_tunnel: action.payload.ssh_tunnel || trimmedState.ssh_tunnel,
query_input,
};
case ActionType.DbSelected:
// set initial state for blank form
return {
...action.payload,
extra: DEFAULT_EXTRA,
expose_in_sqllab: true,
};
case ActionType.ConfigMethodChange:
return {
...action.payload,
};
case ActionType.Reset:
default:
return null;
}
}