Skip to content

Commit

Permalink
feat: support for multi possible values for line item fields
Browse files Browse the repository at this point in the history
  • Loading branch information
Pritesh Hirani committed Dec 12, 2023
1 parent f75b77f commit 46dfa97
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 23 deletions.
38 changes: 26 additions & 12 deletions src/comparators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import { difference, intersection, isEqual } from 'lodash'
import { ALLOWED_DATE_DIFFERENCE_HOURS, MAX_WRONG_TRIGRAMS, MatchKey } from './constants'
import { normalizeForMatch } from './normalize'
import { trigrams } from './trigram'
import { MultiPossibleValues } from './types'

export type ComparisonOptions = {
leeway?: number
allowPartialMatch?: boolean
}

export type ComparisonFn<T = unknown> = (
export type ComparisonFn<T = unknown, U = T> = (
parsed: T,
labeled: T,
labeled: U,
options?: ComparisonOptions
) => MatchKey | null

Expand Down Expand Up @@ -47,22 +48,35 @@ export const compareNumerics: ComparisonFn<number | null> = (parsed, labeled, op
return match
}

export const compareStrings: ComparisonFn<string | null> = (parsed, labeled, options) => {
export const compareStrings: ComparisonFn<string | null, MultiPossibleValues | string | null> = (
parsed,
labeled,
options
) => {
if (parsed === null && labeled === null) return null
if (parsed === null || labeled === null) return MatchKey.NO

const match = fullOrNoMatchComparison(parsed, labeled)
if (match === MatchKey.NO && options?.allowPartialMatch) {
const parsedTrigrams = trigrams(normalizeForMatch(parsed) as string)
const labeledTrigrams = trigrams(normalizeForMatch(labeled) as string)
const diff = difference(labeledTrigrams, parsedTrigrams)
const intersect = intersection(labeledTrigrams, parsedTrigrams)
if (diff.length <= MAX_WRONG_TRIGRAMS && intersect.length > 0) {
return MatchKey.PARTIAL
const labeledInputs = Array.isArray(labeled) ? labeled : [labeled]
let hasPartialMatch = false

for (const labeledInput of labeledInputs) {
const match = fullOrNoMatchComparison(parsed, labeledInput)
if (match === MatchKey.FULL) return match

if (options?.allowPartialMatch) {
const parsedTrigrams = trigrams(normalizeForMatch(parsed) as string)
const labeledTrigrams = trigrams(normalizeForMatch(labeledInput) as string)
const diff = difference(labeledTrigrams, parsedTrigrams)
const intersect = intersection(labeledTrigrams, parsedTrigrams)
if (diff.length <= MAX_WRONG_TRIGRAMS && intersect.length > 0) {
hasPartialMatch = true
}
}
}

return match
if (hasPartialMatch) return MatchKey.PARTIAL

return MatchKey.NO
}

export const compareDates: ComparisonFn<Date | null> = (parsed, labeled, options) => {
Expand Down
43 changes: 42 additions & 1 deletion src/compareWithLabeled.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from './compareWithLabeled'
import { MatchKey } from './constants'
import { createMockComparisonInput, createMockComparisonResult } from './testUtils'
import { ComparisonInput, LineItem } from './types'
import { ComparisonInput, LineItem, LineItemWithMultiPossibleValues } from './types'

describe('createMismatchComment', () => {
it('should return an empty string for a full match', () => {
Expand Down Expand Up @@ -217,6 +217,47 @@ describe('evaluateLineItemFields', () => {
lineItemUrl: { match: null },
})
})

it('should evaluate each field with multiple possible values', () => {
const parsed: LineItem[] = [
{
name: 'success',
color: 'parsed',
productId: 'parsed',
imageUrl: 'parsed',
quantity: null,
size: 'parsed',
unitPrice: 1,
url: null,
},
]

const labeled: LineItemWithMultiPossibleValues[] = [
{
name: ['successful', 'failure'],
color: 'expected',
productId: 'expected',
imageUrl: ['expected', 'not expected'],
quantity: 1,
size: 'expected',
unitPrice: 1,
url: null,
},
]

const result = evaluateLineItemFields(parsed, labeled)

expect(result).toMatchObject({
lineItemName: { match: MatchKey.PARTIAL },
lineItemColor: { match: MatchKey.NO },
lineItemProductId: { match: MatchKey.NO },
lineItemProductImageUrl: { match: MatchKey.NO },
lineItemQuantity: { match: MatchKey.FULL },
lineItemSize: { match: MatchKey.NO },
lineItemUnitPrice: { match: MatchKey.FULL },
lineItemUrl: { match: null },
})
})
})

describe('evaluateLineItemCount', () => {
Expand Down
19 changes: 11 additions & 8 deletions src/compareWithLabeled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
FieldResult,
LineItem,
LineItemFieldResults,
LineItemWithMultiPossibleValues,
} from './types'

type ComparisonResultWithoutAPS = Omit<ComparisonResult, 'APS'>
Expand Down Expand Up @@ -46,7 +47,7 @@ export const createMismatchComment = (
export const evaluateField = (
field: keyof ComparisonInput,
parsed: ComparisonInput,
labeled: ComparisonInput,
labeled: ComparisonInput<LineItemWithMultiPossibleValues>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
comparator: ComparisonFn<any>,
comparatorOptions?: ComparisonOptions
Expand Down Expand Up @@ -121,13 +122,13 @@ export const evaluateArray = <T = unknown>(

export const evaluateCostsAddUp = (
parsed: ComparisonInput,
labeled: ComparisonInput
labeled: ComparisonInput<LineItemWithMultiPossibleValues>
): FieldResult => {
const expectedTotal = labeled?.totalAmount
if (!expectedTotal) return { match: null, comments: ['missing labeled total amount'] }

const calculatedTotalParsed = calculateOrderTotal(parsed)
const calculatedTotalLabeled = calculateOrderTotal(labeled)
const calculatedTotalLabeled = calculateOrderTotal(labeled as ComparisonInput)

const comparisonOptions: ComparisonOptions = {
allowPartialMatch: true,
Expand Down Expand Up @@ -163,7 +164,7 @@ export const evaluateCostsAddUp = (

export const evaluateLineItemCount = (
parsed: ComparisonInput['lineItems'],
labeled: ComparisonInput['lineItems']
labeled: ComparisonInput<LineItemWithMultiPossibleValues>['lineItems']
): FieldResult => {
if (!parsed && !labeled) {
return {
Expand All @@ -187,7 +188,7 @@ export const evaluateLineItemCount = (

export const evaluateLineItemFields = (
parsed: ComparisonInput['lineItems'],
labeled: ComparisonInput['lineItems']
labeled: ComparisonInput<LineItemWithMultiPossibleValues>['lineItems']
): LineItemFieldResults => {
const result: LineItemFieldResults = {
lineItemName: { match: null },
Expand Down Expand Up @@ -230,7 +231,8 @@ export const evaluateLineItemFields = (
},
imageUrl: {
fieldName: 'lineItemProductImageUrl',
comparator: fullOrNoMatchComparison,
comparator: compareStrings,
comparatorOptions: { allowPartialMatch: false },
},
quantity: {
fieldName: 'lineItemQuantity',
Expand All @@ -249,7 +251,8 @@ export const evaluateLineItemFields = (
},
url: {
fieldName: 'lineItemUrl',
comparator: fullOrNoMatchComparison,
comparator: compareStrings,
comparatorOptions: { allowPartialMatch: false },
},
}

Expand Down Expand Up @@ -316,7 +319,7 @@ export const compareWithLabeled = ({
labeled,
}: {
parsed: ComparisonInput
labeled: ComparisonInput
labeled: ComparisonInput<LineItemWithMultiPossibleValues>
}): ComparisonResult => {
const fieldResults: ComparisonResultWithoutAPS = {
status: evaluateField('status', parsed, labeled, compareOrderStatus),
Expand Down
12 changes: 10 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { MatchKey } from './constants'

export type MultiPossibleValues = string[]

export type LineItem = {
color: string | null
imageUrl: string | null
Expand All @@ -11,13 +13,19 @@ export type LineItem = {
url: string | null
}

export type ComparisonInput = {
export type LineItemWithMultiPossibleValues = Omit<LineItem, 'imageUrl' | 'name' | 'url'> & {
imageUrl: MultiPossibleValues | string | null
name: MultiPossibleValues | string | null
url: MultiPossibleValues | string | null
}

export type ComparisonInput<T = LineItem> = {
carriers: string[] | null
coupon: number | null
currency: string | null
discount: number | null
giftCard: number | null
lineItems: LineItem[] | null
lineItems: T[] | null
merchantDomain: string | null
merchantName: string | null
orderDate: Date | null
Expand Down

0 comments on commit 46dfa97

Please sign in to comment.