mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Alerting] change index action config executionTimeField to nullable (#61127)
resolves https://github.com/elastic/kibana/issues/61056 When the index action params moved into config, the `schema.maybe()` on the `executionTimeField` should have been changed to `schema.nullable()`, otherwise you can never "unset" the field, once it's set. Changes rippled down to the UI as well. To be extra safe, we also check that the `executionTimeField` isn't an empty string when trimmed, as ES will not accept a document with a property that is the empty string.
This commit is contained in:
parent
683bf3a72e
commit
aa73e2aee3
7 changed files with 60 additions and 15 deletions
|
@ -52,6 +52,7 @@ describe('config validation', () => {
|
|||
...config,
|
||||
index: 'testing-123',
|
||||
refresh: false,
|
||||
executionTimeField: null,
|
||||
});
|
||||
|
||||
config.executionTimeField = 'field-123';
|
||||
|
@ -62,6 +63,14 @@ describe('config validation', () => {
|
|||
executionTimeField: 'field-123',
|
||||
});
|
||||
|
||||
config.executionTimeField = null;
|
||||
expect(validateConfig(actionType, config)).toEqual({
|
||||
...config,
|
||||
index: 'testing-123',
|
||||
refresh: false,
|
||||
executionTimeField: null,
|
||||
});
|
||||
|
||||
delete config.index;
|
||||
|
||||
expect(() => {
|
||||
|
@ -73,9 +82,11 @@ describe('config validation', () => {
|
|||
|
||||
expect(() => {
|
||||
validateConfig(actionType, { index: 'testing-123', executionTimeField: true });
|
||||
}).toThrowErrorMatchingInlineSnapshot(
|
||||
`"error validating action type config: [executionTimeField]: expected value of type [string] but got [boolean]"`
|
||||
);
|
||||
}).toThrowErrorMatchingInlineSnapshot(`
|
||||
"error validating action type config: [executionTimeField]: types that failed validation:
|
||||
- [executionTimeField.0]: expected value of type [string] but got [boolean]
|
||||
- [executionTimeField.1]: expected value to equal [null]"
|
||||
`);
|
||||
|
||||
delete config.refresh;
|
||||
expect(() => {
|
||||
|
@ -138,12 +149,12 @@ describe('params validation', () => {
|
|||
describe('execute()', () => {
|
||||
test('ensure parameters are as expected', async () => {
|
||||
const secrets = {};
|
||||
let config: ActionTypeConfigType;
|
||||
let config: Partial<ActionTypeConfigType>;
|
||||
let params: ActionParamsType;
|
||||
let executorOptions: ActionTypeExecutorOptions;
|
||||
|
||||
// minimal params
|
||||
config = { index: 'index-value', refresh: false, executionTimeField: undefined };
|
||||
config = { index: 'index-value', refresh: false };
|
||||
params = {
|
||||
documents: [{ jim: 'bob' }],
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@ export type ActionTypeConfigType = TypeOf<typeof ConfigSchema>;
|
|||
const ConfigSchema = schema.object({
|
||||
index: schema.string(),
|
||||
refresh: schema.boolean({ defaultValue: false }),
|
||||
executionTimeField: schema.maybe(schema.string()),
|
||||
executionTimeField: schema.nullable(schema.string()),
|
||||
});
|
||||
|
||||
// params definition
|
||||
|
@ -63,8 +63,9 @@ async function executor(
|
|||
|
||||
const bulkBody = [];
|
||||
for (const document of params.documents) {
|
||||
if (config.executionTimeField != null) {
|
||||
document[config.executionTimeField] = new Date();
|
||||
const timeField = config.executionTimeField == null ? '' : config.executionTimeField.trim();
|
||||
if (timeField !== '') {
|
||||
document[timeField] = new Date();
|
||||
}
|
||||
|
||||
bulkBody.push({ index: {} });
|
||||
|
|
|
@ -52,6 +52,26 @@ describe('index connector validation', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('index connector validation with minimal config', () => {
|
||||
test('connector validation succeeds when connector config is valid', () => {
|
||||
const actionConnector = {
|
||||
secrets: {},
|
||||
id: 'test',
|
||||
actionTypeId: '.index',
|
||||
name: 'es_index',
|
||||
config: {
|
||||
index: 'test_es_index',
|
||||
},
|
||||
} as EsIndexActionConnector;
|
||||
|
||||
expect(actionTypeModel.validateConnector(actionConnector)).toEqual({
|
||||
errors: {
|
||||
index: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('action params validation', () => {
|
||||
test('action params validation succeeds when action params is valid', () => {
|
||||
const actionParams = {
|
||||
|
|
|
@ -79,7 +79,7 @@ const IndexActionConnectorFields: React.FunctionComponent<ActionConnectorFieldsP
|
|||
>> = ({ action, editActionConfig, errors, http }) => {
|
||||
const { index, refresh, executionTimeField } = action.config;
|
||||
const [hasTimeFieldCheckbox, setTimeFieldCheckboxState] = useState<boolean>(
|
||||
executionTimeField !== undefined
|
||||
executionTimeField != null
|
||||
);
|
||||
|
||||
const [indexPatterns, setIndexPatterns] = useState([]);
|
||||
|
@ -206,6 +206,11 @@ const IndexActionConnectorFields: React.FunctionComponent<ActionConnectorFieldsP
|
|||
checked={hasTimeFieldCheckbox || false}
|
||||
onChange={() => {
|
||||
setTimeFieldCheckboxState(!hasTimeFieldCheckbox);
|
||||
// if changing from checked to not checked (hasTimeField === true),
|
||||
// set time field to null
|
||||
if (hasTimeFieldCheckbox) {
|
||||
editActionConfig('executionTimeField', null);
|
||||
}
|
||||
}}
|
||||
label={
|
||||
<>
|
||||
|
@ -245,13 +250,13 @@ const IndexActionConnectorFields: React.FunctionComponent<ActionConnectorFieldsP
|
|||
fullWidth
|
||||
name="executionTimeField"
|
||||
data-test-subj="executionTimeFieldSelect"
|
||||
value={executionTimeField}
|
||||
value={executionTimeField ?? ''}
|
||||
onChange={e => {
|
||||
editActionConfig('executionTimeField', e.target.value);
|
||||
editActionConfig('executionTimeField', nullableString(e.target.value));
|
||||
}}
|
||||
onBlur={() => {
|
||||
if (executionTimeField === undefined) {
|
||||
editActionConfig('executionTimeField', '');
|
||||
editActionConfig('executionTimeField', null);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
@ -312,3 +317,9 @@ const IndexParamsFields: React.FunctionComponent<ActionParamsProps<IndexActionPa
|
|||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
// if the string == null or is empty, return null, else return string
|
||||
function nullableString(str: string | null | undefined) {
|
||||
if (str == null || str.trim() === '') return null;
|
||||
return str;
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ export interface EmailActionConnector extends ActionConnector {
|
|||
|
||||
interface EsIndexConfig {
|
||||
index: string;
|
||||
executionTimeField?: string;
|
||||
executionTimeField?: string | null;
|
||||
refresh?: boolean;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ export default function indexTest({ getService }: FtrProviderContext) {
|
|||
config: {
|
||||
index: ES_TEST_INDEX_NAME,
|
||||
refresh: false,
|
||||
executionTimeField: null,
|
||||
},
|
||||
});
|
||||
createdActionID = createdAction.id;
|
||||
|
@ -58,7 +59,7 @@ export default function indexTest({ getService }: FtrProviderContext) {
|
|||
id: fetchedAction.id,
|
||||
name: 'An index action',
|
||||
actionTypeId: '.index',
|
||||
config: { index: ES_TEST_INDEX_NAME, refresh: false },
|
||||
config: { index: ES_TEST_INDEX_NAME, refresh: false, executionTimeField: null },
|
||||
});
|
||||
|
||||
// create action with all config props
|
||||
|
|
|
@ -43,6 +43,7 @@ export default function indexTest({ getService }: FtrProviderContext) {
|
|||
config: {
|
||||
index: ES_TEST_INDEX_NAME,
|
||||
refresh: false,
|
||||
executionTimeField: null,
|
||||
},
|
||||
});
|
||||
createdActionID = createdAction.id;
|
||||
|
@ -56,7 +57,7 @@ export default function indexTest({ getService }: FtrProviderContext) {
|
|||
id: fetchedAction.id,
|
||||
name: 'An index action',
|
||||
actionTypeId: '.index',
|
||||
config: { index: ES_TEST_INDEX_NAME, refresh: false },
|
||||
config: { index: ES_TEST_INDEX_NAME, refresh: false, executionTimeField: null },
|
||||
});
|
||||
|
||||
// create action with all config props
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue