@things-factory/organization 10.1.218 → 10.1.222
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-server/service/audience/audience-member.d.ts +29 -0
- package/dist-server/service/audience/audience-member.js +84 -0
- package/dist-server/service/audience/audience-member.js.map +1 -0
- package/dist-server/service/audience/audience.d.ts +43 -0
- package/dist-server/service/audience/audience.js +81 -0
- package/dist-server/service/audience/audience.js.map +1 -0
- package/dist-server/service/index.d.ts +2 -0
- package/dist-server/service/index.js +2 -0
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/tests/audience.test.ts +85 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/organization",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.222",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"browser": "dist-client/index.js",
|
|
6
6
|
"things-factory": true,
|
|
@@ -41,5 +41,5 @@
|
|
|
41
41
|
"@things-factory/contact": "^10.1.218",
|
|
42
42
|
"@things-factory/shell": "^10.1.208"
|
|
43
43
|
},
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "afdd6a9b1c8b440e39d4fec48bfbad1a0c02fccc"
|
|
45
45
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whom an audience names (ADR-0163 decision 2) - the one rule a shared dashboard card and an announcement ask.
|
|
3
|
+
*/
|
|
4
|
+
import { audienceReaches, audienceTargetRefusal, departmentsOfEmployees, withAncestors, withDescendants } from '../server/service/audience/audience'
|
|
5
|
+
|
|
6
|
+
/* 본부 > 영업팀 > 영업1파트, and 경영지원 on its own. */
|
|
7
|
+
const TREE = new Map<string, string | null>([
|
|
8
|
+
['hq', null],
|
|
9
|
+
['sales', 'hq'],
|
|
10
|
+
['sales-1', 'sales'],
|
|
11
|
+
['admin', null]
|
|
12
|
+
])
|
|
13
|
+
|
|
14
|
+
const member = (departmentIds: string[], roleIds: string[] = []) => ({ userId: 'u-me', domainId: 'd1', departmentIds, roleIds })
|
|
15
|
+
|
|
16
|
+
describe('who an audience reaches', () => {
|
|
17
|
+
it('★ the domain, a person, a department one stands in, a role one holds - and no one else', () => {
|
|
18
|
+
const me = member(['sales'], ['role-hr'])
|
|
19
|
+
expect(audienceReaches([{ scope: 'DOMAIN', targetId: 'd1' }], me)).toBe(true)
|
|
20
|
+
expect(audienceReaches([{ scope: 'DOMAIN', targetId: 'd2' }], me)).toBe(false)
|
|
21
|
+
expect(audienceReaches([{ scope: 'USER', targetId: 'u-me' }], me)).toBe(true)
|
|
22
|
+
expect(audienceReaches([{ scope: 'USER', targetId: 'u-other' }], me)).toBe(false)
|
|
23
|
+
expect(audienceReaches([{ scope: 'DEPARTMENT', targetId: 'sales' }], me)).toBe(true)
|
|
24
|
+
expect(audienceReaches([{ scope: 'DEPARTMENT', targetId: 'admin' }], me)).toBe(false)
|
|
25
|
+
expect(audienceReaches([{ scope: 'ROLE', targetId: 'role-hr' }], me)).toBe(true)
|
|
26
|
+
expect(audienceReaches([{ scope: 'ROLE', targetId: 'role-finance' }], me)).toBe(false)
|
|
27
|
+
expect(audienceReaches([], me)).toBe(false)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('★ a department reaches the people of the departments under it (the user, 2026-10-26), not those above or beside', () => {
|
|
31
|
+
const inPart = member(withAncestors(['sales-1'], TREE))
|
|
32
|
+
expect(audienceReaches([{ scope: 'DEPARTMENT', targetId: 'hq' }], inPart)).toBe(true)
|
|
33
|
+
expect(audienceReaches([{ scope: 'DEPARTMENT', targetId: 'sales' }], inPart)).toBe(true)
|
|
34
|
+
const inTeam = member(withAncestors(['sales'], TREE))
|
|
35
|
+
expect(audienceReaches([{ scope: 'DEPARTMENT', targetId: 'sales-1' }], inTeam)).toBe(false)
|
|
36
|
+
expect(audienceReaches([{ scope: 'DEPARTMENT', targetId: 'admin' }], inTeam)).toBe(false)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('a person without a user or a domain is reached by nothing', () => {
|
|
40
|
+
expect(audienceReaches([{ scope: 'DOMAIN', targetId: 'd1' }], { userId: '', domainId: 'd1', departmentIds: [], roleIds: [] })).toBe(false)
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
describe('the department tree', () => {
|
|
45
|
+
it('★ up from a department to the top, and down to every department under it', () => {
|
|
46
|
+
expect(withAncestors(['sales-1'], TREE).sort()).toEqual(['hq', 'sales', 'sales-1'])
|
|
47
|
+
expect(withDescendants(['hq'], TREE).sort()).toEqual(['hq', 'sales', 'sales-1'])
|
|
48
|
+
expect(withDescendants(['admin'], TREE)).toEqual(['admin'])
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('a loop in the tree ends the walk', () => {
|
|
52
|
+
const loop = new Map<string, string | null>([
|
|
53
|
+
['a', 'b'],
|
|
54
|
+
['b', 'a']
|
|
55
|
+
])
|
|
56
|
+
expect(withAncestors(['a'], loop).sort()).toEqual(['a', 'b'])
|
|
57
|
+
expect(withDescendants(['a'], loop).sort()).toEqual(['a', 'b'])
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('★ only active employee records count, and a record without a department adds none', () => {
|
|
61
|
+
expect(
|
|
62
|
+
departmentsOfEmployees([
|
|
63
|
+
{ active: true, department: { id: 'sales' } },
|
|
64
|
+
{ active: false, department: { id: 'admin' } },
|
|
65
|
+
{ active: null, department: { id: 'hq' } },
|
|
66
|
+
{ active: true, department: null },
|
|
67
|
+
{ active: true, department: { id: 'sales' } }
|
|
68
|
+
])
|
|
69
|
+
).toEqual(['sales'])
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
describe('what may be addressed', () => {
|
|
74
|
+
const domain = { domainId: 'd1', departmentIds: ['hq', 'sales'], roleIds: ['role-hr'] }
|
|
75
|
+
it('★ a known scope with a target of this domain', () => {
|
|
76
|
+
expect(audienceTargetRefusal({ scope: 'DOMAIN', targetId: 'd1' }, domain)).toBeUndefined()
|
|
77
|
+
expect(audienceTargetRefusal({ scope: 'DOMAIN', targetId: 'd2' }, domain)).toBe('another-domain')
|
|
78
|
+
expect(audienceTargetRefusal({ scope: 'DEPARTMENT', targetId: 'sales' }, domain)).toBeUndefined()
|
|
79
|
+
expect(audienceTargetRefusal({ scope: 'DEPARTMENT', targetId: 'elsewhere' }, domain)).toBe('unknown-department')
|
|
80
|
+
expect(audienceTargetRefusal({ scope: 'ROLE', targetId: 'role-hr' }, domain)).toBeUndefined()
|
|
81
|
+
expect(audienceTargetRefusal({ scope: 'ROLE', targetId: 'role-x' }, domain)).toBe('unknown-role')
|
|
82
|
+
expect(audienceTargetRefusal({ scope: 'TEAM', targetId: 'x' }, domain)).toBe('unknown-scope')
|
|
83
|
+
expect(audienceTargetRefusal({ scope: 'USER', targetId: '' }, domain)).toBe('no-target')
|
|
84
|
+
})
|
|
85
|
+
})
|