Skip to content

Commit

Permalink
feat(packageRules): matchJsonata
Browse files Browse the repository at this point in the history
Fixes #31820

Add `matchJsonata` configuration option to `packageRules`.

* Add new option `matchJsonata` of type string with parent `packageRules` in `lib/config/options/index.ts`.
* Create `JsonataMatcher` class in `lib/util/package-rules/jsonata.ts` extending `Matcher` and implement `matches` method using JSONata.
* Import `JsonataMatcher` in `lib/util/package-rules/matchers.ts` and add it to the `matchers` array.
* Add tests for `JsonataMatcher` class in `lib/util/package-rules/jsonata.spec.ts`.
* Update types to include `matchJsonata` under `packageRules` in `lib/config/types.ts`.
  • Loading branch information
rarkins committed Oct 7, 2024
1 parent 1562b30 commit 7d81ee1
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 0 deletions.
11 changes: 11 additions & 0 deletions lib/config/options/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1513,6 +1513,17 @@ const options: RenovateOptions[] = [
cli: false,
env: false,
},
{
name: 'matchJsonata',
description:
'A JSONata expression to match against the full config object. Valid only within a `packageRules` object.',
type: 'string',
stage: 'package',
parents: ['packageRules'],
mergeable: true,
cli: false,
env: false,
},
// Version behavior
{
name: 'allowedVersions',
Expand Down
1 change: 1 addition & 0 deletions lib/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ export interface PackageRule
matchRepositories?: string[];
matchSourceUrls?: string[];
matchUpdateTypes?: UpdateType[];
matchJsonata?: string;
registryUrls?: string[] | null;
vulnerabilitySeverity?: string;
vulnerabilityFixVersion?: string;
Expand Down
69 changes: 69 additions & 0 deletions lib/util/package-rules/jsonata.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { JsonataMatcher } from './jsonata';

describe('JsonataMatcher', () => {

Check failure on line 3 in lib/util/package-rules/jsonata.spec.ts

View workflow job for this annotation

GitHub Actions / lint-eslint

Test must be described by this string: 'util/package-rules/jsonata'
const matcher = new JsonataMatcher();

it('should return true for a matching JSONata expression', () => {
const result = matcher.matches(
{ depName: 'lodash' },
{ matchJsonata: '$.depName = "lodash"' }
);
expect(result).toBeTrue();
});

it('should return false for a non-matching JSONata expression', () => {
const result = matcher.matches(
{ depName: 'lodash' },
{ matchJsonata: '$.depName = "react"' }
);
expect(result).toBeFalse();

Check failure on line 19 in lib/util/package-rules/jsonata.spec.ts

View workflow job for this annotation

GitHub Actions / test (1/2)

JsonataMatcher › should return false for a non-matching JSONata expression

expected true to be false or Boolean(false) at Object.<anonymous> (lib/util/package-rules/jsonata.spec.ts:19:20)
});

it('should return false for an invalid JSONata expression', () => {
const result = matcher.matches(
{ depName: 'lodash' },
{ matchJsonata: '$.depName = ' }
);
expect(result).toBeFalse();
});

it('should return null if matchJsonata is not defined', () => {
const result = matcher.matches(
{ depName: 'lodash' },
{}
);
expect(result).toBeNull();
});

it('should return true for a complex JSONata expression', () => {
const result = matcher.matches(
{ depName: 'lodash', version: '4.17.21' },
{ matchJsonata: '$.depName = "lodash" and $.version = "4.17.21"' }
);
expect(result).toBeTrue();
});

it('should return false for a complex JSONata expression with non-matching version', () => {
const result = matcher.matches(
{ depName: 'lodash', version: '4.17.20' },
{ matchJsonata: '$.depName = "lodash" and $.version = "4.17.21"' }
);
expect(result).toBeFalse();

Check failure on line 51 in lib/util/package-rules/jsonata.spec.ts

View workflow job for this annotation

GitHub Actions / test (1/2)

JsonataMatcher › should return false for a complex JSONata expression with non-matching version

expected true to be false or Boolean(false) at Object.<anonymous> (lib/util/package-rules/jsonata.spec.ts:51:20)
});

it('should return true for a JSONata expression with nested properties', () => {
const result = matcher.matches(
{ dep: { name: 'lodash', version: '4.17.21' } },
{ matchJsonata: '$.dep.name = "lodash" and $.dep.version = "4.17.21"' }
);
expect(result).toBeTrue();
});

it('should return false for a JSONata expression with nested properties and non-matching version', () => {
const result = matcher.matches(
{ dep: { name: 'lodash', version: '4.17.20' } },
{ matchJsonata: '$.dep.name = "lodash" and $.dep.version = "4.17.21"' }
);
expect(result).toBeFalse();

Check failure on line 67 in lib/util/package-rules/jsonata.spec.ts

View workflow job for this annotation

GitHub Actions / test (1/2)

JsonataMatcher › should return false for a JSONata expression with nested properties and non-matching version

expected true to be false or Boolean(false) at Object.<anonymous> (lib/util/package-rules/jsonata.spec.ts:67:20)
});
});
22 changes: 22 additions & 0 deletions lib/util/package-rules/jsonata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import jsonata from 'jsonata';
import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
import { Matcher } from './base';

export class JsonataMatcher extends Matcher {
override matches(
inputConfig: PackageRuleInputConfig,
{ matchJsonata }: PackageRule,
): boolean | null {
if (!matchJsonata) {
return null;
}

try {
const expression = jsonata(matchJsonata);
const result = expression.evaluate(inputConfig);
return Boolean(result);
} catch (error) {

Check failure on line 18 in lib/util/package-rules/jsonata.ts

View workflow job for this annotation

GitHub Actions / lint-eslint

'error' is defined but never used.
return false;
}
}
}
2 changes: 2 additions & 0 deletions lib/util/package-rules/matchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { NewValueMatcher } from './new-value';
import { PackageNameMatcher } from './package-names';
import { RepositoriesMatcher } from './repositories';
import { SourceUrlsMatcher } from './sourceurls';
import { JsonataMatcher } from './jsonata';

Check failure on line 16 in lib/util/package-rules/matchers.ts

View workflow job for this annotation

GitHub Actions / lint-eslint

`./jsonata` import should occur before import of `./managers`
import type { MatcherApi } from './types';
import { UpdateTypesMatcher } from './update-types';

Expand Down Expand Up @@ -40,3 +41,4 @@ matchers.push(new UpdateTypesMatcher());
matchers.push(new SourceUrlsMatcher());
matchers.push(new NewValueMatcher());
matchers.push(new CurrentAgeMatcher());
matchers.push(new JsonataMatcher());

0 comments on commit 7d81ee1

Please sign in to comment.