@things-factory/meta-ui 10.1.37 → 10.1.38
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-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/index.d.ts +1 -1
- package/dist-server/service/meta-activity/meta-activity-mutation.d.ts +1 -1
- package/dist-server/service/meta-activity/meta-activity-mutation.js +27 -7
- package/dist-server/service/meta-activity/meta-activity-mutation.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -16
- package/tests/delete-meta-activities.test.ts +49 -0
- package/tests/meta-activity-domain.test.ts +122 -0
- package/translations/en.json +1 -0
- package/translations/ko.json +1 -0
- package/translations/ms.json +1 -0
- package/translations/zh.json +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/meta-ui",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.38",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"browser": "dist-client/index.js",
|
|
6
6
|
"things-factory": true,
|
|
@@ -41,24 +41,24 @@
|
|
|
41
41
|
"@operato/styles": "^10.0.0",
|
|
42
42
|
"@operato/time-calculator": "^10.0.0",
|
|
43
43
|
"@operato/utils": "^10.0.0",
|
|
44
|
-
"@things-factory/auth-base": "^10.1.
|
|
45
|
-
"@things-factory/code-base": "^10.1.
|
|
44
|
+
"@things-factory/auth-base": "^10.1.38",
|
|
45
|
+
"@things-factory/code-base": "^10.1.38",
|
|
46
46
|
"@things-factory/env": "^10.1.20",
|
|
47
|
-
"@things-factory/export-base": "^10.1.
|
|
47
|
+
"@things-factory/export-base": "^10.1.38",
|
|
48
48
|
"@things-factory/form-ui": "^10.1.29",
|
|
49
|
-
"@things-factory/id-rule-base": "^10.1.
|
|
50
|
-
"@things-factory/import-base": "^10.1.
|
|
51
|
-
"@things-factory/import-ui": "^10.1.
|
|
52
|
-
"@things-factory/integration-base": "^10.1.
|
|
53
|
-
"@things-factory/lite-menu": "^10.1.
|
|
54
|
-
"@things-factory/menu-base": "^10.1.
|
|
55
|
-
"@things-factory/more-base": "^10.1.
|
|
56
|
-
"@things-factory/personalization": "^10.1.
|
|
57
|
-
"@things-factory/resource-base": "^10.1.
|
|
58
|
-
"@things-factory/shell": "^10.1.
|
|
49
|
+
"@things-factory/id-rule-base": "^10.1.38",
|
|
50
|
+
"@things-factory/import-base": "^10.1.38",
|
|
51
|
+
"@things-factory/import-ui": "^10.1.38",
|
|
52
|
+
"@things-factory/integration-base": "^10.1.38",
|
|
53
|
+
"@things-factory/lite-menu": "^10.1.38",
|
|
54
|
+
"@things-factory/menu-base": "^10.1.38",
|
|
55
|
+
"@things-factory/more-base": "^10.1.38",
|
|
56
|
+
"@things-factory/personalization": "^10.1.38",
|
|
57
|
+
"@things-factory/resource-base": "^10.1.38",
|
|
58
|
+
"@things-factory/shell": "^10.1.38",
|
|
59
59
|
"@things-factory/utils": "^10.1.20",
|
|
60
|
-
"@things-factory/worklist": "^10.1.
|
|
60
|
+
"@things-factory/worklist": "^10.1.38",
|
|
61
61
|
"moment-timezone": "^0.5.45"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "b947349fe1c27d11038b592ceadaba336f14785b"
|
|
64
64
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { join } from 'path'
|
|
2
|
+
|
|
3
|
+
import { buildSchema } from 'type-graphql'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* deleteMetaActivities deletes activity definitions in bulk. Its neighbour updateMultipleMetaActivity
|
|
7
|
+
* asked the activity privilege and it did not; closed 2026-09-24 (architect ruling).
|
|
8
|
+
*
|
|
9
|
+
* Measured from the schema meta-ui boots from, walked by auth-base's privilege directive - the
|
|
10
|
+
* compiled output, since the entities need emitDecoratorMetadata (auth-base tests/compiled.ts).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { schema: declared } = require(join(__dirname, '..', 'dist-server', 'service', 'index.js'))
|
|
14
|
+
const { privilegeDirectiveResolver, privilegeOfField } = require(
|
|
15
|
+
join(__dirname, '..', '..', 'auth-base', 'dist-server', 'service', 'privilege', 'privilege-directive.js')
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
const schema = await buildSchema({
|
|
20
|
+
resolvers: declared.resolverClasses,
|
|
21
|
+
validate: false,
|
|
22
|
+
pubSub: { publish() {}, subscribe() {} } as any
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
privilegeDirectiveResolver(schema)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('asks the same privilege as the other activity definition writes', () => {
|
|
29
|
+
expect(privilegeOfField('Mutation', 'deleteMetaActivities')).toEqual({
|
|
30
|
+
known: true,
|
|
31
|
+
gate: { category: 'activity', privilege: 'mutation', owner: true, super: false }
|
|
32
|
+
})
|
|
33
|
+
expect(privilegeOfField('Mutation', 'updateMultipleMetaActivity')).toEqual(privilegeOfField('Mutation', 'deleteMetaActivities'))
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('looks each activity up in the caller\'s domain only', () => {
|
|
37
|
+
/*
|
|
38
|
+
* Unnarrowed, an id from another tenant removed that tenant's template from the process-wide
|
|
39
|
+
* installations. Read off the compiled body: the lookup runs inside a transaction this test cannot
|
|
40
|
+
* stand up without a database.
|
|
41
|
+
*/
|
|
42
|
+
const body = require('fs').readFileSync(
|
|
43
|
+
join(__dirname, '..', 'dist-server', 'service', 'meta-activity', 'meta-activity-mutation.js'),
|
|
44
|
+
'utf8'
|
|
45
|
+
)
|
|
46
|
+
const deleteBody = body.slice(body.indexOf('async deleteMetaActivities('), body.indexOf('async installActivityTemplate('))
|
|
47
|
+
|
|
48
|
+
expect(deleteBody).toMatch(/findOne\(\{\s*where: \{ domain: \{ id: domain\.id \}, id: ids\[i\] \}/)
|
|
49
|
+
})
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { join } from 'path'
|
|
2
|
+
|
|
3
|
+
import { buildSchema } from 'type-graphql'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* meta-ui's activity writes stay in the caller's domain. Ruled 2026-09-24.
|
|
7
|
+
*
|
|
8
|
+
* installActivityTemplate asks the activity privilege, as the other writes do - measured from the
|
|
9
|
+
* schema. And an id from another tenant, for an activity or for the menu it embeds, is not reached:
|
|
10
|
+
* the compiled resolver is called with a transaction whose repositories answer only rows of the
|
|
11
|
+
* domain they are asked for.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const DIST = join(__dirname, '..', 'dist-server')
|
|
15
|
+
const { schema: declared } = require(join(DIST, 'service', 'index.js'))
|
|
16
|
+
const { MetaActivityMutation } = require(join(DIST, 'service', 'meta-activity', 'meta-activity-mutation.js'))
|
|
17
|
+
const { privilegeDirectiveResolver, privilegeOfField } = require(
|
|
18
|
+
join(__dirname, '..', '..', 'auth-base', 'dist-server', 'service', 'privilege', 'privilege-directive.js')
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
beforeAll(async () => {
|
|
22
|
+
const schema = await buildSchema({
|
|
23
|
+
resolvers: declared.resolverClasses,
|
|
24
|
+
validate: false,
|
|
25
|
+
pubSub: { publish() {}, subscribe() {} } as any
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
privilegeDirectiveResolver(schema)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('installActivityTemplate asks the same privilege as the other activity writes', () => {
|
|
32
|
+
expect(privilegeOfField('Mutation', 'installActivityTemplate')).toEqual({
|
|
33
|
+
known: true,
|
|
34
|
+
gate: { category: 'activity', privilege: 'mutation', owner: true, super: false }
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
/* Rows by entity name; a lookup that names no domain gets nothing, so an unnarrowed query shows. */
|
|
39
|
+
function transaction(rows: Record<string, any[]>, saved: any[], asked: any[]) {
|
|
40
|
+
return {
|
|
41
|
+
getRepository: (entity: any) => ({
|
|
42
|
+
findOne: async ({ where }: any) => {
|
|
43
|
+
asked.push({ entity: entity.name, where })
|
|
44
|
+
return (rows[entity.name] || []).find(one => one.id === where?.id && one.domainId === where?.domain?.id) || null
|
|
45
|
+
},
|
|
46
|
+
find: async ({ where }: any) => {
|
|
47
|
+
asked.push({ entity: entity.name, where })
|
|
48
|
+
return (rows[entity.name] || []).filter(one => one.domainId === where?.domain?.id)
|
|
49
|
+
},
|
|
50
|
+
save: async (row: any) => (saved.push(row), row)
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const context = (tx: any) =>
|
|
56
|
+
({
|
|
57
|
+
state: { domain: { id: 'd-here' }, user: { id: 'u-me' }, tx },
|
|
58
|
+
t: (key: string) => key
|
|
59
|
+
}) as any
|
|
60
|
+
|
|
61
|
+
const rows = {
|
|
62
|
+
Activity: [
|
|
63
|
+
{ id: 'act-here', domainId: 'd-here', name: 'Mine' },
|
|
64
|
+
{ id: 'act-other', domainId: 'd-other', name: 'Theirs' }
|
|
65
|
+
],
|
|
66
|
+
Menu: [
|
|
67
|
+
{ id: 'menu-here', domainId: 'd-here', name: 'mine', routing: 'mine' },
|
|
68
|
+
{ id: 'menu-other', domainId: 'd-other', name: 'theirs', routing: 'theirs' }
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
describe('updateMultipleMetaActivity', () => {
|
|
73
|
+
it("another tenant's activity id is refused by name, and nothing is saved", async () => {
|
|
74
|
+
const saved: any[] = []
|
|
75
|
+
const tx = transaction(rows, saved, [])
|
|
76
|
+
|
|
77
|
+
await expect(
|
|
78
|
+
new MetaActivityMutation().updateMultipleMetaActivity([{ id: 'act-other', cuFlag: 'M' }], context(tx))
|
|
79
|
+
).rejects.toThrow('error.meta-activity not in this domain')
|
|
80
|
+
expect(saved).toEqual([])
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it("this domain's activity is updated", async () => {
|
|
84
|
+
const saved: any[] = []
|
|
85
|
+
const tx = transaction(rows, saved, [])
|
|
86
|
+
|
|
87
|
+
await new MetaActivityMutation().updateMultipleMetaActivity([{ id: 'act-here', cuFlag: 'M', name: 'Mine2' }], context(tx))
|
|
88
|
+
expect(saved.map(one => one.id)).toEqual(['act-here'])
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it("another tenant's menu is not embedded in the model", async () => {
|
|
92
|
+
const saved: any[] = []
|
|
93
|
+
const tx = transaction(rows, saved, [])
|
|
94
|
+
|
|
95
|
+
await new MetaActivityMutation().updateMultipleMetaActivity(
|
|
96
|
+
[{ id: 'act-here', cuFlag: 'M', menu: { id: 'menu-other' } }],
|
|
97
|
+
context(tx)
|
|
98
|
+
)
|
|
99
|
+
expect(JSON.stringify(saved[0].model)).not.toContain('theirs')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it("this domain's menu is embedded", async () => {
|
|
103
|
+
const saved: any[] = []
|
|
104
|
+
const tx = transaction(rows, saved, [])
|
|
105
|
+
|
|
106
|
+
await new MetaActivityMutation().updateMultipleMetaActivity(
|
|
107
|
+
[{ id: 'act-here', cuFlag: 'M', menu: { id: 'menu-here' } }],
|
|
108
|
+
context(tx)
|
|
109
|
+
)
|
|
110
|
+
expect(JSON.stringify(saved[0].model)).toContain('mine')
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
describe('installActivityTemplate', () => {
|
|
115
|
+
it("installs from this domain's released activities only", async () => {
|
|
116
|
+
const asked: any[] = []
|
|
117
|
+
const tx = transaction(rows, [], asked)
|
|
118
|
+
|
|
119
|
+
await new MetaActivityMutation().installActivityTemplate(context(tx))
|
|
120
|
+
expect(asked.filter(one => one.entity === 'Activity').every(one => one.where?.domain?.id === 'd-here')).toBe(true)
|
|
121
|
+
})
|
|
122
|
+
})
|
package/translations/en.json
CHANGED
package/translations/ko.json
CHANGED
package/translations/ms.json
CHANGED