Skip to content

Commit

Permalink
[Ingest pipelines] Use human-readable database names in IP location p…
Browse files Browse the repository at this point in the history
…rocessor form (#197413)

Fixes #196768

## Summary

This PR adds human-readable labels in the Database field in the IP
location processor form.



https://github.com/user-attachments/assets/94fb5e22-ccae-4bff-b6fb-92ae0cf9449c
  • Loading branch information
ElenaStoeva authored Oct 24, 2024
1 parent 66b2447 commit 9c92b52
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 10 deletions.
5 changes: 5 additions & 0 deletions x-pack/plugins/ingest_pipelines/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ export enum FieldCopyAction {

export type DatabaseType = 'maxmind' | 'ipinfo' | 'web' | 'local' | 'unknown';

export interface DatabaseNameOption {
value: string;
text: string;
}

export interface GeoipDatabase {
name: string;
id: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { FieldsConfig, from, to } from './shared';
import { TargetField } from './common_fields/target_field';
import { PropertiesField } from './common_fields/properties_field';
import type { GeoipDatabase } from '../../../../../../../common/types';
import { getDatabaseText, getDatabaseValue } from '../../../../../sections/manage_processors/utils';
import { getTypeLabel } from '../../../../../sections/manage_processors/constants';

const extension = '.mmdb';
Expand All @@ -33,8 +34,20 @@ const fieldsConfig: FieldsConfig = {
/* Optional field config */
database_file: {
type: FIELD_TYPES.COMBO_BOX,
deserializer: (v: unknown) => to.arrayOfStrings(v).map((str) => str?.split(extension)[0]),
serializer: (v: string[]) => (v.length ? `${v[0]}${extension}` : undefined),
deserializer: (v: unknown) =>
to.arrayOfStrings(v).map((str) => {
const databaseName = str?.split(extension)[0];
// Use the translated text for this database, if it exists
return getDatabaseText(databaseName) ?? databaseName;
}),
serializer: (v: any[]) => {
if (v.length) {
const databaseName = v[0];
const databaseValue = getDatabaseValue(databaseName);
return databaseValue ? `${databaseValue}${extension}` : `${databaseName}${extension}`;
}
return undefined;
},
label: i18n.translate('xpack.ingestPipelines.pipelineEditor.ipLocationForm.databaseFileLabel', {
defaultMessage: 'Database file (optional)',
}),
Expand Down Expand Up @@ -77,7 +90,8 @@ export const IpLocation: FunctionComponent = () => {
const dataAsOptions = (data || []).map((item) => ({
id: item.id,
type: item.type,
label: item.name,
// Use the translated text for this database, if it exists
label: getDatabaseText(item.name) ?? item.name,
}));
const optionsByGroup = groupBy(dataAsOptions, 'type');
const groupedOptions = map(optionsByGroup, (items, groupName) => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { i18n } from '@kbn/i18n';
import type { GeoipDatabase } from '../../../../common/types';
import type { GeoipDatabase, DatabaseNameOption } from '../../../../common/types';

export const ADD_DATABASE_MODAL_TITLE_ID = 'manageProcessorsAddGeoipDatabase';
export const ADD_DATABASE_MODAL_FORM_ID = 'manageProcessorsAddGeoipDatabaseForm';
Expand All @@ -24,7 +24,7 @@ export const DATABASE_TYPE_OPTIONS = [
}),
},
];
export const GEOIP_NAME_OPTIONS = [
export const GEOIP_NAME_OPTIONS: DatabaseNameOption[] = [
{
value: 'GeoIP2-Anonymous-IP',
text: i18n.translate('xpack.ingestPipelines.manageProcessors.geoip.anonymousIPDatabaseName', {
Expand Down Expand Up @@ -71,7 +71,7 @@ export const GEOIP_NAME_OPTIONS = [
}),
},
];
export const IPINFO_NAME_OPTIONS = [
export const IPINFO_NAME_OPTIONS: DatabaseNameOption[] = [
{
value: 'asn',
text: i18n.translate('xpack.ingestPipelines.manageProcessors.ipinfo.freeAsnDatabaseName', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { css } from '@emotion/react';

import { IPINFO_NAME_OPTIONS } from './constants';
import { getDatabaseText } from './utils';
import type { GeoipDatabase } from '../../../../common/types';
import { SectionLoading, useKibana } from '../../../shared_imports';
import { getTypeLabel } from './constants';
Expand Down Expand Up @@ -71,9 +71,8 @@ export const GeoipList: React.FunctionComponent = () => {
sortable: true,
render: (name: string, row) => {
if (row.type === 'ipinfo') {
// find the name in the options to get the translated value
const option = IPINFO_NAME_OPTIONS.find((opt) => opt.value === name);
return option?.text ?? name;
// Use the translated text for this database, if it exists
return getDatabaseText(name, 'ipinfo') ?? name;
}

return name;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { getDatabaseValue, getDatabaseText } from './utils';

describe('getDatabaseValue', () => {
it('should return the value for a given database text for maxmind', () => {
const databaseText = 'GeoIP2 City';
const result = getDatabaseValue(databaseText, 'maxmind');
expect(result).toBe('GeoIP2-City');
});

it('should return the value for a given database text for ipinfo', () => {
const databaseText = 'Free IP to ASN';
const result = getDatabaseValue(databaseText, 'ipinfo');
expect(result).toBe('asn');
});

it('should return undefined if the database text is not found', () => {
const databaseText = 'Unknown Database';
const result = getDatabaseValue(databaseText);
expect(result).toBeUndefined();
});

it('should return the value when no type is provided and the database text is found in any option', () => {
const databaseText = 'ASN';
const result = getDatabaseValue(databaseText);
expect(result).toBe('standard_asn');
});
});

describe('getDatabaseText', () => {
it('should return the human-readable name for a given database value for maxmind', () => {
const databaseValue = 'GeoIP2-City';
const result = getDatabaseText(databaseValue, 'maxmind');
expect(result).toBe('GeoIP2 City');
});

it('should return the human-readable name for a given database value for ipinfo', () => {
const databaseValue = 'asn';
const result = getDatabaseText(databaseValue, 'ipinfo');
expect(result).toBe('Free IP to ASN');
});

it('should return undefined if the database value is not found', () => {
const databaseValue = 'unknown-value';
const result = getDatabaseText(databaseValue);
expect(result).toBeUndefined();
});

it('should return the human-readable name when no type is provided and the value is found in any option', () => {
const databaseValue = 'standard_location';
const result = getDatabaseText(databaseValue);
expect(result).toBe('IP Geolocation');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { DatabaseType, DatabaseNameOption } from '../../../../common/types';
import { GEOIP_NAME_OPTIONS, IPINFO_NAME_OPTIONS } from './constants';

const getDatabaseNameOptions = (type?: DatabaseType): DatabaseNameOption[] => {
switch (type) {
case 'maxmind':
return GEOIP_NAME_OPTIONS;
case 'ipinfo':
return IPINFO_NAME_OPTIONS;
case undefined:
return [...GEOIP_NAME_OPTIONS, ...IPINFO_NAME_OPTIONS];
default:
return [];
}
};

/**
* Returns the value/id of the database, if it exists.
*
* @param databaseText The human-readable name of the database
* @param type If specified, searches only in the database name options for this type
*/
export const getDatabaseValue = (databaseText: string, type?: DatabaseType): string | undefined => {
const options = getDatabaseNameOptions(type);
return options.find((opt) => opt.text === databaseText)?.value;
};

/**
* Returns the human-readable name of the database, if it exists.
*
* @param databaseText The id/value of the database
* @param type If specified, searches only in the database name options for this type
*/
export const getDatabaseText = (databaseValue: string, type?: DatabaseType): string | undefined => {
const options = getDatabaseNameOptions(type);
return options.find((opt) => opt.value === databaseValue)?.text;
};

0 comments on commit 9c92b52

Please sign in to comment.