Skip to content

Commit 0488bdc

Browse files
sdurnovДурнов Сергей Юрьевич
andauthored
feat: provide catch-all function to process dep rule (#246)
Co-authored-by: Дурнов Сергей Юрьевич <durnov.sergey@rwb.ru>
1 parent 33a7db4 commit 0488bdc

4 files changed

Lines changed: 125 additions & 13 deletions

File tree

packages/core/src/lib/checks/check-for-dependency-rule-violation.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,14 @@ export function checkForDependencyRuleViolation(
5656
toTags,
5757
config.depRules,
5858
{
59+
from: '',
60+
to: '',
5961
fromModulePath: fromModule,
6062
toModulePath: toFsPath(importedModulePath),
6163
fromFilePath: fsPath,
6264
toFilePath: toFsPath(importedModulePath),
65+
fromTags,
66+
toTags,
6367
},
6468
);
6569

packages/core/src/lib/checks/is-dependency-allowed.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ export const isDependencyAllowed = (
2626
return true;
2727
} else if (
2828
typeof matcher === 'function' &&
29-
matcher({ from, to, ...context })
29+
matcher({
30+
...context,
31+
from,
32+
to,
33+
})
3034
) {
3135
return true;
3236
}

packages/core/src/lib/checks/tests/is-dependency-allowed.spec.ts

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,29 @@ import '../../test/expect.extensions';
1212

1313
type TestParams = [string, boolean][];
1414

15-
const dummyContext: DependencyCheckContext = {
15+
const createMockDependencyCheckContext = (
16+
overrides?: Partial<DependencyCheckContext>,
17+
): DependencyCheckContext =>
18+
({
19+
from: '',
20+
to: '',
21+
fromModulePath: '',
22+
toModulePath: '',
23+
fromFilePath: '',
24+
toFilePath: '',
25+
fromTags: [],
26+
toTags: [],
27+
...overrides,
28+
}) as DependencyCheckContext;
29+
30+
const dummyContext: DependencyCheckContext = createMockDependencyCheckContext({
1631
fromModulePath: '/project/moduleFrom' as FsPath,
1732
toModulePath: '/project/moduleTo' as FsPath,
1833
fromFilePath: '/project/moduleFrom/some.component.ts' as FsPath,
1934
toFilePath: '/project/cool.service.ts' as FsPath,
20-
};
35+
fromTags: ['domain:customers'],
36+
toTags: ['domain:holidays'],
37+
});
2138

2239
const createAssertsForConfig = (config: DependencyRulesConfig) => {
2340
return {
@@ -27,7 +44,7 @@ const createAssertsForConfig = (config: DependencyRulesConfig) => {
2744
from,
2845
Array.isArray(to) ? to : [to],
2946
config,
30-
{} as DependencyCheckContext,
47+
createMockDependencyCheckContext(),
3148
),
3249
).toBe(true);
3350
},
@@ -37,7 +54,7 @@ const createAssertsForConfig = (config: DependencyRulesConfig) => {
3754
from,
3855
Array.isArray(to) ? to : [to],
3956
config,
40-
{} as DependencyCheckContext,
57+
createMockDependencyCheckContext(),
4158
),
4259
).toBe(false);
4360
},
@@ -48,7 +65,7 @@ const createAssertsForConfig = (config: DependencyRulesConfig) => {
4865
from,
4966
Array.isArray(to) ? to : [to],
5067
config,
51-
{} as DependencyCheckContext,
68+
createMockDependencyCheckContext(),
5269
),
5370
).toBe(expected);
5471
},
@@ -113,9 +130,9 @@ describe('check dependency rules', () => {
113130
{
114131
'domain:customers': (context) => {
115132
expect(context).toStrictEqual({
133+
...dummyContext,
116134
from: 'domain:customers',
117135
to: 'domain:holidays',
118-
...dummyContext,
119136
});
120137
return true;
121138
},
@@ -210,4 +227,91 @@ describe('check dependency rules', () => {
210227
assertInvalid('type:model', toTag);
211228
},
212229
);
230+
231+
describe('fromTags and toTags', () => {
232+
const crossDomainConfig: DependencyRulesConfig = {
233+
'*': ({ fromTags, toTags }) => {
234+
const fromDomain = fromTags.find((t) => t.startsWith('domain:'));
235+
const toDomain = toTags.find((t) => t.startsWith('domain:'));
236+
const fromType = fromTags.find((t) => t.startsWith('type:'));
237+
const toType = toTags.find((t) => t.startsWith('type:'));
238+
239+
if (toDomain === 'domain:shared') return true;
240+
if (fromType === 'type:api' || toType === 'type:api') return true;
241+
if (fromDomain === toDomain) return true;
242+
243+
return false;
244+
},
245+
};
246+
247+
it('should allow access to shared domain from any domain', () => {
248+
expect(
249+
isDependencyAllowed(
250+
'domain:customers',
251+
['domain:shared'],
252+
crossDomainConfig,
253+
createMockDependencyCheckContext({
254+
fromTags: ['domain:customers'],
255+
toTags: ['domain:shared'],
256+
}),
257+
),
258+
).toBe(true);
259+
});
260+
261+
it('should forbid cross-domain access except for API', () => {
262+
expect(
263+
isDependencyAllowed(
264+
'domain:customers',
265+
['domain:holidays'],
266+
crossDomainConfig,
267+
createMockDependencyCheckContext({
268+
fromTags: ['domain:customers'],
269+
toTags: ['domain:holidays'],
270+
}),
271+
),
272+
).toBe(false);
273+
});
274+
275+
it('should allow API access across domains', () => {
276+
expect(
277+
isDependencyAllowed(
278+
'type:api',
279+
['domain:holidays'],
280+
crossDomainConfig,
281+
createMockDependencyCheckContext({
282+
fromTags: ['type:api', 'domain:customers'],
283+
toTags: ['domain:holidays', 'type:ui'],
284+
}),
285+
),
286+
).toBe(true);
287+
});
288+
289+
it('should allow access to API in another domain', () => {
290+
expect(
291+
isDependencyAllowed(
292+
'domain:customers',
293+
['type:api'],
294+
crossDomainConfig,
295+
createMockDependencyCheckContext({
296+
fromTags: ['domain:customers', 'type:feature'],
297+
toTags: ['type:api', 'domain:holidays'],
298+
}),
299+
),
300+
).toBe(true);
301+
});
302+
303+
it('should allow same domain access', () => {
304+
expect(
305+
isDependencyAllowed(
306+
'domain:customers',
307+
['domain:customers'],
308+
crossDomainConfig,
309+
createMockDependencyCheckContext({
310+
fromTags: ['domain:customers'],
311+
toTags: ['domain:customers'],
312+
}),
313+
),
314+
).toBe(true);
315+
});
316+
});
213317
});
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import { FsPath } from '../file-info/fs-path';
22

33
export interface DependencyCheckContext {
4+
from: string;
5+
to: string;
46
fromModulePath: FsPath;
57
toModulePath: FsPath;
68
fromFilePath: FsPath;
79
toFilePath: FsPath;
10+
fromTags: string[];
11+
toTags: string[];
812
}
913

10-
export type RuleMatcherFn = (
11-
context: {
12-
from: string;
13-
to: string;
14-
} & DependencyCheckContext,
15-
) => boolean;
14+
export type RuleMatcherFn = (context: DependencyCheckContext) => boolean;
15+
1616
export type RuleMatcher = string | null | RuleMatcherFn;
1717
export type DependencyRulesConfig = Record<string, RuleMatcher | RuleMatcher[]>;

0 commit comments

Comments
 (0)