@things-factory/code-base 10.1.47 → 10.1.105
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/common-code/common-code-mutation.js +30 -4
- package/dist-server/service/common-code/common-code-mutation.js.map +1 -1
- package/dist-server/service/common-code-detail/common-code-detail-mutation.js +30 -6
- package/dist-server/service/common-code-detail/common-code-detail-mutation.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/tests/domain-reference.test.ts +169 -0
- package/tests/open-doors.test.ts +27 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/code-base",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.105",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"browser": "dist-client/index.js",
|
|
6
6
|
"things-factory": true,
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@operato/graphql": "^10.0.0",
|
|
31
|
-
"@things-factory/auth-base": "^10.1.
|
|
32
|
-
"@things-factory/shell": "^10.1.
|
|
31
|
+
"@things-factory/auth-base": "^10.1.79",
|
|
32
|
+
"@things-factory/shell": "^10.1.79"
|
|
33
33
|
},
|
|
34
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "c8ad9b40d57709beefd5e3d6167fd9cab6efea3a"
|
|
35
35
|
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A common code and its details are looked up inside the caller's domain.
|
|
3
|
+
*
|
|
4
|
+
* The lookups asked by id alone: a detail could be put under another domain's code group, a group could take
|
|
5
|
+
* over another domain's details, and the grid save (`updateMultiple…`, cuFlag 'M') overwrote a row of another
|
|
6
|
+
* domain by its id. This runs the real mutations on sqlite and reads the rows back.
|
|
7
|
+
*/
|
|
8
|
+
import 'reflect-metadata'
|
|
9
|
+
|
|
10
|
+
const { existsSync } = require('fs')
|
|
11
|
+
const { join } = require('path')
|
|
12
|
+
const { DataSource } = require('typeorm')
|
|
13
|
+
|
|
14
|
+
const shell = require('@things-factory/shell')
|
|
15
|
+
const authBase = require('@things-factory/auth-base')
|
|
16
|
+
|
|
17
|
+
function loadCompiled(modulePath: string): any {
|
|
18
|
+
const resolved = join(__dirname, '..', 'dist-server', `${modulePath}.js`)
|
|
19
|
+
if (!existsSync(resolved)) {
|
|
20
|
+
throw new Error(`code-base build output is missing: ${resolved}\nBuild it first: npm run build:server`)
|
|
21
|
+
}
|
|
22
|
+
return require(resolved)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let dataSource: any
|
|
26
|
+
let CommonCode: any
|
|
27
|
+
let CommonCodeDetail: any
|
|
28
|
+
let CommonCodeMutation: any
|
|
29
|
+
let CommonCodeDetailMutation: any
|
|
30
|
+
|
|
31
|
+
let domain: any
|
|
32
|
+
let other: any
|
|
33
|
+
let user: any
|
|
34
|
+
let ourCode: any
|
|
35
|
+
let theirCode: any
|
|
36
|
+
let ourDetail: any
|
|
37
|
+
let theirDetail: any
|
|
38
|
+
|
|
39
|
+
beforeAll(async () => {
|
|
40
|
+
const codeBase = loadCompiled('service/index')
|
|
41
|
+
CommonCode = codeBase.CommonCode
|
|
42
|
+
CommonCodeDetail = codeBase.CommonCodeDetail
|
|
43
|
+
CommonCodeMutation = loadCompiled('service/common-code/common-code-mutation').CommonCodeMutation
|
|
44
|
+
CommonCodeDetailMutation = loadCompiled('service/common-code-detail/common-code-detail-mutation').CommonCodeDetailMutation
|
|
45
|
+
|
|
46
|
+
dataSource = new DataSource({
|
|
47
|
+
type: 'sqlite',
|
|
48
|
+
database: ':memory:',
|
|
49
|
+
synchronize: true,
|
|
50
|
+
dropSchema: true,
|
|
51
|
+
namingStrategy: new shell.NamingStrategy(),
|
|
52
|
+
entities: [...shell.entities, ...authBase.entities, ...codeBase.entities],
|
|
53
|
+
logging: false
|
|
54
|
+
})
|
|
55
|
+
await dataSource.initialize()
|
|
56
|
+
shell.addDataSource('default', dataSource)
|
|
57
|
+
shell.addDataSource('tx', dataSource)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
beforeEach(async () => {
|
|
61
|
+
await dataSource.synchronize(true)
|
|
62
|
+
const domains = dataSource.getRepository(shell.Domain)
|
|
63
|
+
domain = await domains.save({ name: 'ours', subdomain: 'ours' })
|
|
64
|
+
other = await domains.save({ name: 'theirs', subdomain: 'theirs' })
|
|
65
|
+
user = await dataSource.getRepository(authBase.User).save({ name: 'tester', email: 'tester@code-base.test', locale: 'en' })
|
|
66
|
+
|
|
67
|
+
const codes = dataSource.getRepository(CommonCode)
|
|
68
|
+
ourCode = await codes.save({ domain, name: 'UNIT', description: 'ours' })
|
|
69
|
+
theirCode = await codes.save({ domain: other, name: 'UNIT', description: 'theirs' })
|
|
70
|
+
|
|
71
|
+
const details = dataSource.getRepository(CommonCodeDetail)
|
|
72
|
+
ourDetail = await details.save({ domain, commonCode: ourCode, name: 'EA', description: 'ours', rank: 1 })
|
|
73
|
+
theirDetail = await details.save({ domain: other, commonCode: theirCode, name: 'EA', description: 'theirs', rank: 1 })
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
afterAll(async () => {
|
|
77
|
+
if (!dataSource) return
|
|
78
|
+
shell.removeDataSource('default')
|
|
79
|
+
shell.removeDataSource('tx')
|
|
80
|
+
await dataSource.destroy()
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const context = () => ({ state: { domain, user, tx: dataSource.manager } })
|
|
84
|
+
|
|
85
|
+
async function groupOf(detailId: string) {
|
|
86
|
+
return (await dataSource.getRepository(CommonCodeDetail).findOneBy({ id: detailId }))?.commonCodeId ?? null
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
describe("a detail's code group comes from the caller's domain", () => {
|
|
90
|
+
it('a group of this domain is accepted', async () => {
|
|
91
|
+
const created = await new CommonCodeDetailMutation().createCommonCodeDetail(
|
|
92
|
+
{ name: 'BOX', description: 'box', rank: 2, commonCode: { id: ourCode.id } },
|
|
93
|
+
context()
|
|
94
|
+
)
|
|
95
|
+
expect(await groupOf(created.id)).toBe(ourCode.id)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('createCommonCodeDetail refuses a group of another domain', async () => {
|
|
99
|
+
await expect(
|
|
100
|
+
new CommonCodeDetailMutation().createCommonCodeDetail(
|
|
101
|
+
{ name: 'BOX', description: 'box', rank: 2, commonCode: { id: theirCode.id } },
|
|
102
|
+
context()
|
|
103
|
+
)
|
|
104
|
+
).rejects.toThrow(/common code not found in this domain/)
|
|
105
|
+
expect(await dataSource.getRepository(CommonCodeDetail).countBy({ name: 'BOX' })).toBe(0)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('updateCommonCodeDetail refuses a group of another domain', async () => {
|
|
109
|
+
await expect(
|
|
110
|
+
new CommonCodeDetailMutation().updateCommonCodeDetail(ourDetail.id, { commonCode: { id: theirCode.id } }, context())
|
|
111
|
+
).rejects.toThrow(/common code not found in this domain/)
|
|
112
|
+
expect(await groupOf(ourDetail.id)).toBe(ourCode.id)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('updateMultipleCommonCodeDetail refuses it on a new row and on an edited one', async () => {
|
|
116
|
+
const mutation = new CommonCodeDetailMutation()
|
|
117
|
+
await expect(
|
|
118
|
+
mutation.updateMultipleCommonCodeDetail(
|
|
119
|
+
[{ cuFlag: '+', name: 'BOX', rank: 2, labels: {}, commonCode: { id: theirCode.id } }],
|
|
120
|
+
context()
|
|
121
|
+
)
|
|
122
|
+
).rejects.toThrow(/common code not found in this domain/)
|
|
123
|
+
await expect(
|
|
124
|
+
mutation.updateMultipleCommonCodeDetail(
|
|
125
|
+
[{ cuFlag: 'M', id: ourDetail.id, labels: {}, commonCode: { id: theirCode.id } }],
|
|
126
|
+
context()
|
|
127
|
+
)
|
|
128
|
+
).rejects.toThrow(/common code not found in this domain/)
|
|
129
|
+
expect(await groupOf(ourDetail.id)).toBe(ourCode.id)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it("updateMultipleCommonCodeDetail does not edit another domain's detail by its id", async () => {
|
|
133
|
+
await expect(
|
|
134
|
+
new CommonCodeDetailMutation().updateMultipleCommonCodeDetail(
|
|
135
|
+
[{ cuFlag: 'M', id: theirDetail.id, labels: {}, description: 'overwritten' }],
|
|
136
|
+
context()
|
|
137
|
+
)
|
|
138
|
+
).rejects.toThrow(/common code detail not found in this domain/)
|
|
139
|
+
expect((await dataSource.getRepository(CommonCodeDetail).findOneBy({ id: theirDetail.id })).description).toBe('theirs')
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
describe("a code group's details and rows come from the caller's domain", () => {
|
|
144
|
+
it('createCommonCode refuses a detail of another domain', async () => {
|
|
145
|
+
await expect(
|
|
146
|
+
new CommonCodeMutation().createCommonCode({ name: 'COLOR', description: 'color', details: [theirDetail.id] }, context())
|
|
147
|
+
).rejects.toThrow(/common code detail not found in this domain/)
|
|
148
|
+
expect(await groupOf(theirDetail.id)).toBe(theirCode.id)
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('updateCommonCode refuses a detail of another domain', async () => {
|
|
152
|
+
await expect(
|
|
153
|
+
new CommonCodeMutation().updateCommonCode('UNIT', { details: [theirDetail.id] }, context())
|
|
154
|
+
).rejects.toThrow(/common code detail not found in this domain/)
|
|
155
|
+
expect(await groupOf(theirDetail.id)).toBe(theirCode.id)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('updateCommonCode keeps the details of this domain', async () => {
|
|
159
|
+
const updated = await new CommonCodeMutation().updateCommonCode('UNIT', { details: [ourDetail.id] }, context())
|
|
160
|
+
expect(updated.details.map((detail: any) => detail.id)).toEqual([ourDetail.id])
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it("updateMultipleCommonCode does not edit another domain's code by its id", async () => {
|
|
164
|
+
await expect(
|
|
165
|
+
new CommonCodeMutation().updateMultipleCommonCode([{ cuFlag: 'M', id: theirCode.id, description: 'overwritten' }], context())
|
|
166
|
+
).rejects.toThrow(/common code not found in this domain/)
|
|
167
|
+
expect((await dataSource.getRepository(CommonCode).findOneBy({ id: theirCode.id })).description).toBe('theirs')
|
|
168
|
+
})
|
|
169
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Every mutation of code-base asks for `common-code:mutation`, and the domain owner may call it.
|
|
3
|
+
*
|
|
4
|
+
* Until 2026-10-01 none declared anything: any member of a domain could create, rename or delete its
|
|
5
|
+
* common codes and their details. The queries are not gated yet and are not measured here.
|
|
6
|
+
*/
|
|
7
|
+
import { gatesOf, openFields } from '../../../test/helpers/open-doors'
|
|
8
|
+
|
|
9
|
+
let gates: Awaited<ReturnType<typeof gatesOf>>
|
|
10
|
+
|
|
11
|
+
beforeAll(async () => {
|
|
12
|
+
gates = await gatesOf('code-base')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('no mutation is open', () => {
|
|
16
|
+
expect(openFields(gates.Mutation)).toEqual([])
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('every mutation asks for common-code:mutation and admits the domain owner', () => {
|
|
20
|
+
for (const [field, gate] of Object.entries(gates.Mutation)) {
|
|
21
|
+
expect({ field, ...gate }).toEqual({ field, category: 'common-code', privilege: 'mutation', owner: true, super: false })
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('the walk reaches every mutation', () => {
|
|
26
|
+
expect(Object.keys(gates.Mutation)).toHaveLength(12)
|
|
27
|
+
})
|