Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance @replace directive #1901

Merged
merged 7 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions packages/core/src/mapping-kit/__tests__/index.iso.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,74 @@ describe('@replace', () => {
)
expect(output).toStrictEqual('different+things')
})
test('replace boolean', () => {
const payload = {
a: true
}
const output = transform(
{
'@replace': {
pattern: 'true',
replacement: 'granted',
value: { '@path': '$.a' }
}
},
payload
)
expect(output).toStrictEqual('granted')
})
test('replace number', () => {
const payload = {
a: 1
}
const output = transform(
{
'@replace': {
pattern: '1',
replacement: 'granted',
value: { '@path': '$.a' }
}
},
payload
)
expect(output).toStrictEqual('granted')
})
test('replace 2 values', () => {
const payload = {
a: 'something-great!'
}
const output = transform(
{
'@replace': {
pattern: '-',
replacement: ' ',
pattern2: 'great',
replacement2: 'awesome',
value: { '@path': '$.a' }
}
},
payload
)
expect(output).toStrictEqual('something awesome!')
})
test('replace with 2 values but only second one exists', () => {
const payload = {
a: false
}
const output = transform(
{
'@replace': {
pattern: 'true',
replacement: 'granted',
pattern2: 'false',
replacement2: 'denied',
value: { '@path': '$.a' }
}
},
payload
)
expect(output).toStrictEqual('denied')
})
})

describe('remove undefined values in objects', () => {
Expand Down
55 changes: 41 additions & 14 deletions packages/core/src/mapping-kit/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,23 @@ registerDirective('@case', (opts, payload) => {

export const MAX_PATTERN_LENGTH = 10
export const MAX_REPLACEMENT_LENGTH = 10

function performReplace(value: string, pattern: string, replacement: string, flags: string) {
if (pattern.length > MAX_PATTERN_LENGTH) {
throw new Error(`@replace requires a "pattern" less than ${MAX_PATTERN_LENGTH} characters`)
}

if (replacement.length > MAX_REPLACEMENT_LENGTH) {
throw new Error(`@replace requires a "replacement" less than ${MAX_REPLACEMENT_LENGTH} characters`)
}

// We don't want users providing regular expressions for the pattern (for now)
// https://stackoverflow.com/questions/F3115150/how-to-escape-regular-expression-special-characters-using-javascript
pattern = pattern.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')

return value.replace(new RegExp(pattern, flags), replacement)
}

registerDirective('@replace', (opts, payload) => {
if (!isObject(opts)) {
throw new Error('@replace requires an object with a "pattern" key')
Expand All @@ -117,6 +134,12 @@ registerDirective('@replace', (opts, payload) => {
opts.replacement = ''
}

// Assume null/missing replacement means empty for pattern2 if exists
if (opts.pattern2 && opts.replacement2 == null) {
// Empty replacement string is ok
opts.replacement2 = ''
}

// case sensitive by default if this key is missing
if (opts.ignorecase == null) {
opts.ignorecase = false
Expand All @@ -127,39 +150,43 @@ registerDirective('@replace', (opts, payload) => {
opts.global = true
}

let pattern = opts.pattern
const pattern = opts.pattern
const replacement = opts.replacement
const ignorecase = opts.ignorecase
const isGlobal = opts.global
if (opts.value) {
const value = resolve(opts.value, payload)
let value = resolve(opts.value, payload)
let new_value = ''

// We want to be able to replace values that are boolean or numbers
if (typeof value === 'boolean' || typeof value === 'number') {
value = String(value)
}

if (
typeof value === 'string' &&
typeof pattern === 'string' &&
typeof replacement === 'string' &&
typeof ignorecase === 'boolean' &&
typeof isGlobal === 'boolean'
) {
if (pattern.length > MAX_PATTERN_LENGTH) {
throw new Error(`@replace requires a "pattern" less than ${MAX_PATTERN_LENGTH} characters`)
}

if (replacement.length > MAX_REPLACEMENT_LENGTH) {
throw new Error(`@replace requires a "replacement" less than ${MAX_REPLACEMENT_LENGTH} characters`)
}

// We don't want users providing regular expressions for the pattern (for now)
// https://stackoverflow.com/questions/F3115150/how-to-escape-regular-expression-special-characters-using-javascript
pattern = pattern.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
let flags = ''
if (isGlobal) {
flags += 'g'
}
if (ignorecase) {
flags += 'i'
}
return value.replace(new RegExp(pattern, flags), replacement)

new_value = performReplace(value, pattern, replacement, flags)

// If pattern2 exists, replace the new_value with replacement2
if (opts.pattern2 && typeof opts.pattern2 === 'string' && typeof opts.replacement2 === 'string') {
new_value = performReplace(new_value, opts.pattern2, opts.replacement2, flags)
}
}

return new_value
}
})

Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/mapping-kit/value-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ export interface ReplaceDirective extends DirectiveMetadata {
ignorecase: PrimitiveValue
pattern: string
replacement: string
pattern2: string
replacement2: string
value?: FieldValue
}
}
Expand Down
Loading