import findKey from 'lodash/findKey'; import moment from 'moment'; import CustomEventDateRange from '../../date-range-picker/models/custom-event-date-range'; import {DateRangeType, TimeUnit} from '../../date-range-picker/models/types'; import CustomEventPropertyFilter from './custom-event-property-filter'; import { BooleanOperator, FilterOperand, FilterOperator, FilterValue, ListOperator, NumberOperator, PropertyType, ResourceType, StringOperator, } from './types'; export const PROPERTY_TYPE_MAP: {[key in PropertyType]: string} = { [PropertyType.Boolean]: `boolean`, [PropertyType.Datetime]: `datetime`, [PropertyType.List]: `list`, [PropertyType.Number]: `number`, [PropertyType.String]: `string`, }; export const RESOURCE_TYPE_MAP: {[key in ResourceType]: string} = { [ResourceType.Event]: `properties`, [ResourceType.User]: `user`, }; const DATETIME_OPERATOR_MAP: {[key in DateRangeType]?: string} = { [DateRangeType.Between]: `><`, [DateRangeType.On]: `==`, [DateRangeType.RelativeAfter]: `>`, [DateRangeType.RelativeBefore]: `<`, }; const LIST_OPERATOR_MAP: {[key in ListOperator]?: string} = { [ListOperator.Contains]: `in`, }; const NUMBER_OPERATOR_MAP: {[key in NumberOperator]?: string} = { [NumberOperator.Between]: `><`, [NumberOperator.EqualTo]: `==`, [NumberOperator.GreaterThan]: `>`, [NumberOperator.LessThan]: `<`, }; const STRING_OPERATOR_MAP: {[key in StringOperator]: string} = { [StringOperator.Contains]: `in`, [StringOperator.DoesNotContain]: `not in`, [StringOperator.DoesNotEqual]: `!=`, [StringOperator.Equals]: `==`, [StringOperator.NotSet]: `not set`, [StringOperator.Set]: `set`, }; const TIME_UNIT_MAP: {[key in TimeUnit]?: string} = { [TimeUnit.Day]: `days`, [TimeUnit.Week]: `weeks`, [TimeUnit.Month]: `months`, }; function findKeyByValue(obj: {[key in K]?: string}, value: string): K { return findKey(obj, v => v === value) as K; } interface SegfilterFilter { operand: string|number|Array; operator?: string; unit?: string; } interface Property { type: string; name: string; source: string; } export interface Segfilter { property: Property; type: string; selected_property_type: string; filter: SegfilterFilter; } export function convertSegfilterToPropertyFilter(segfilter: Segfilter): CustomEventPropertyFilter { return new CustomEventPropertyFilter({ propertyDefaultType: findKeyByValue(PROPERTY_TYPE_MAP, segfilter.selected_property_type), propertyName: segfilter.property.name, propertyType: findKeyByValue(PROPERTY_TYPE_MAP, segfilter.type), resourceType: findKeyByValue(RESOURCE_TYPE_MAP, segfilter.property.source), ...toPropertyFilterFilter(segfilter), }); } export function convertPropertyFilterToSegfilter(propertyFilter: CustomEventPropertyFilter): Segfilter { /* eslint-disable camelcase */ return { filter: toSegfilterFilter(propertyFilter), property: { name: propertyFilter.propertyName, source: RESOURCE_TYPE_MAP[propertyFilter.resourceType], type: PROPERTY_TYPE_MAP[propertyFilter.propertyDefaultType], }, selected_property_type: PROPERTY_TYPE_MAP[propertyFilter.propertyDefaultType], type: PROPERTY_TYPE_MAP[propertyFilter.propertyType], }; /* eslint-enable camelcase */ } function toPropertyFilterFilter( segfilter: Segfilter, ): {filterOperator: FilterOperator, filterValue: FilterValue} { const {filter} = segfilter; let filterOperator; let filterValue = null; switch (segfilter.type) { case `boolean`: { switch (filter.operand) { case `false`: filterOperator = BooleanOperator.False; filterValue = false; break; case `true`: filterOperator = BooleanOperator.True; filterValue = true; break; default: break; } break; } case `datetime`: { filterOperator = findKeyByValue(DATETIME_OPERATOR_MAP, filter.operator); switch (filterOperator) { case DateRangeType.RelativeAfter: case DateRangeType.RelativeBefore: { filterValue = new CustomEventDateRange({ type: filterOperator, window: { unit: findKeyByValue(TIME_UNIT_MAP, filter.unit), value: filter.operand as number, }, }); break; } case DateRangeType.On: case DateRangeType.Between: { let operand1 = null; let operand2 = null; if (filterOperator === DateRangeType.On) { const operand = filter.operand as string; [operand1, operand2] = [operand, operand]; } else { [operand1, operand2] = filter.operand as Array; } filterValue = new CustomEventDateRange({ from: segfilterDateToPropertyFilter(operand1), to: segfilterDateToPropertyFilter(operand2), type: filterOperator, }); break; } default: break; } break; } case `list`: { filterOperator = findKeyByValue(LIST_OPERATOR_MAP, filter.operator); filterValue = filter.operand; break; } case `number`: { filterOperator = findKeyByValue(NUMBER_OPERATOR_MAP, filter.operator); switch (filterOperator as NumberOperator) { case NumberOperator.Between: { const operand = filter.operand as Array; filterValue = [Number(operand[0]), Number(operand[1])]; break; } case NumberOperator.EqualTo: case NumberOperator.GreaterThan: case NumberOperator.LessThan: { filterValue = Number(filter.operand); break; } default: break; } break; } case `string`: { filterOperator = findKeyByValue(STRING_OPERATOR_MAP, filter.operator); switch (filterOperator as StringOperator) { case StringOperator.Equals: case StringOperator.DoesNotEqual: { filterValue = filter.operand as Array; break; } case StringOperator.Contains: case StringOperator.DoesNotContain: { filterValue = filter.operand as string; break; } default: break; } break; } default: break; } return {filterOperator, filterValue}; } function toSegfilterFilter(propertyFilter: CustomEventPropertyFilter): SegfilterFilter { const {filterOperator, propertyType} = propertyFilter; let {filterValue} = propertyFilter; switch (propertyType) { case PropertyType.Boolean: { let operand = null; switch (filterOperator as BooleanOperator) { case BooleanOperator.False: { operand = `false`; break; } case BooleanOperator.True: { operand = `true`; break; } default: break; } return {operand}; } case PropertyType.Datetime: { const dateRange = filterValue as CustomEventDateRange; const operator = DATETIME_OPERATOR_MAP[filterOperator as DateRangeType]; let operand; let unit; switch (filterOperator as DateRangeType) { case DateRangeType.RelativeAfter: case DateRangeType.RelativeBefore: { operand = dateRange.window.value; unit = TIME_UNIT_MAP[dateRange.window.unit]; break; } case DateRangeType.On: operand = propertyFilterDateToSegfilter(dateRange.from); break; case DateRangeType.Between: { operand = [propertyFilterDateToSegfilter(dateRange.from), propertyFilterDateToSegfilter(dateRange.to)]; break; } default: break; } return {operator, operand, ...(unit ? {unit} : {})}; } case PropertyType.List: { return { operand: filterValue as string, operator: LIST_OPERATOR_MAP[filterOperator as ListOperator], }; } case PropertyType.Number: { const operator = NUMBER_OPERATOR_MAP[filterOperator as NumberOperator]; let operand; switch (filterOperator as NumberOperator) { case NumberOperator.Between: { filterValue = filterValue as Array; operand = [filterValue[0].toString(), filterValue[1].toString()]; break; } case NumberOperator.EqualTo: case NumberOperator.GreaterThan: case NumberOperator.LessThan: { operand = filterValue.toString(); break; } default: break; } return {operator, operand}; } case PropertyType.String: { const operator = STRING_OPERATOR_MAP[filterOperator as StringOperator]; let operand; switch (filterOperator as StringOperator) { case StringOperator.Equals: case StringOperator.DoesNotEqual: { operand = filterValue as Array; break; } case StringOperator.Contains: case StringOperator.DoesNotContain: { operand = filterValue as string; break; } case StringOperator.NotSet: case StringOperator.Set: { operand = ``; break; } default: break; } return {operator, operand}; } default: return null; } } const SEGFILTER_DATE_FORMAT = `MM/DD/YYYY`; const PROPERTY_FILTER_DATE_FORMAT = `YYYY-MM-DD`; function propertyFilterDateToSegfilter(dateOperand: string): string { return moment.utc(dateOperand, PROPERTY_FILTER_DATE_FORMAT).format(SEGFILTER_DATE_FORMAT); } function segfilterDateToPropertyFilter(dateOperand: string): string { return moment.utc(dateOperand, SEGFILTER_DATE_FORMAT).format(PROPERTY_FILTER_DATE_FORMAT); }