@jskit-ai/workspaces-core 0.1.33 → 0.1.35
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/package.descriptor.mjs +5 -5
- package/package.json +9 -9
- package/src/server/common/repositories/workspaceInvitesRepository.js +17 -21
- package/src/server/common/repositories/workspaceMembershipsRepository.js +25 -22
- package/src/server/common/repositories/workspacesRepository.js +45 -45
- package/src/server/workspaceSettings/workspaceSettingsRepository.js +14 -15
- package/test/workspaceInvitesRepository.test.js +28 -46
- package/test/workspaceMembershipsRepository.test.js +51 -41
- package/test/workspaceSettingsRepository.test.js +15 -36
- package/test/workspacesRepository.test.js +30 -55
package/package.descriptor.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
packageVersion: 1,
|
|
3
3
|
packageId: "@jskit-ai/workspaces-core",
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.35",
|
|
5
5
|
kind: "runtime",
|
|
6
6
|
description: "Workspace tenancy runtime plus HTTP routes, role catalog, and workspace config scaffolding.",
|
|
7
7
|
dependsOn: [
|
|
@@ -116,10 +116,10 @@ export default Object.freeze({
|
|
|
116
116
|
mutations: {
|
|
117
117
|
dependencies: {
|
|
118
118
|
runtime: {
|
|
119
|
-
"@jskit-ai/json-rest-api-core": "0.1.
|
|
120
|
-
"@jskit-ai/resource-core": "0.1.
|
|
121
|
-
"@jskit-ai/resource-crud-core": "0.1.
|
|
122
|
-
"@jskit-ai/users-core": "0.1.
|
|
119
|
+
"@jskit-ai/json-rest-api-core": "0.1.4",
|
|
120
|
+
"@jskit-ai/resource-core": "0.1.4",
|
|
121
|
+
"@jskit-ai/resource-crud-core": "0.1.4",
|
|
122
|
+
"@jskit-ai/users-core": "0.1.69"
|
|
123
123
|
},
|
|
124
124
|
dev: {}
|
|
125
125
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/workspaces-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.35",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -17,14 +17,14 @@
|
|
|
17
17
|
"./shared/resources/workspaceSettingsResource": "./src/shared/resources/workspaceSettingsResource.js"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@jskit-ai/auth-core": "0.1.
|
|
21
|
-
"@jskit-ai/database-runtime": "0.1.
|
|
22
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
23
|
-
"@jskit-ai/json-rest-api-core": "0.1.
|
|
24
|
-
"@jskit-ai/kernel": "0.1.
|
|
25
|
-
"@jskit-ai/resource-crud-core": "0.1.
|
|
26
|
-
"@jskit-ai/resource-core": "0.1.
|
|
27
|
-
"@jskit-ai/users-core": "0.1.
|
|
20
|
+
"@jskit-ai/auth-core": "0.1.58",
|
|
21
|
+
"@jskit-ai/database-runtime": "0.1.59",
|
|
22
|
+
"@jskit-ai/http-runtime": "0.1.58",
|
|
23
|
+
"@jskit-ai/json-rest-api-core": "0.1.4",
|
|
24
|
+
"@jskit-ai/kernel": "0.1.59",
|
|
25
|
+
"@jskit-ai/resource-crud-core": "0.1.4",
|
|
26
|
+
"@jskit-ai/resource-core": "0.1.4",
|
|
27
|
+
"@jskit-ai/users-core": "0.1.69",
|
|
28
28
|
"json-rest-schema": "1.x.x"
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
createJsonApiInputRecord,
|
|
13
13
|
createJsonApiRelationship,
|
|
14
14
|
createJsonRestContext,
|
|
15
|
-
|
|
15
|
+
extractJsonRestCollectionRows
|
|
16
16
|
} from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
|
|
17
17
|
|
|
18
18
|
const RESOURCE_TYPE = "workspaceInvites";
|
|
@@ -105,19 +105,19 @@ function createRepository({ api, knex } = {}) {
|
|
|
105
105
|
const withTransaction = createWithTransaction(knex);
|
|
106
106
|
|
|
107
107
|
async function queryInvites(filters = {}, options = {}, { includeWorkspace = false } = {}) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
108
|
+
return extractJsonRestCollectionRows(
|
|
109
|
+
await api.resources.workspaceInvites.query(
|
|
110
|
+
{
|
|
111
|
+
queryParams: {
|
|
112
|
+
filters,
|
|
113
|
+
...(includeWorkspace ? { include: ["workspace"] } : {})
|
|
114
|
+
},
|
|
115
|
+
transaction: options?.trx || null,
|
|
116
|
+
simplified: true
|
|
113
117
|
},
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
},
|
|
117
|
-
createJsonRestContext(options?.context || null)
|
|
118
|
+
createJsonRestContext(options?.context || null)
|
|
119
|
+
)
|
|
118
120
|
);
|
|
119
|
-
|
|
120
|
-
return Array.isArray(simplifyJsonApiDocument(result)) ? simplifyJsonApiDocument(result) : [];
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
async function findPendingByTokenHash(tokenHash, options = {}) {
|
|
@@ -213,13 +213,12 @@ function createRepository({ api, knex } = {}) {
|
|
|
213
213
|
})
|
|
214
214
|
}
|
|
215
215
|
),
|
|
216
|
-
transaction: options?.trx || null
|
|
217
|
-
simplified: false
|
|
216
|
+
transaction: options?.trx || null
|
|
218
217
|
},
|
|
219
218
|
createJsonRestContext(options?.context || null)
|
|
220
219
|
);
|
|
221
220
|
|
|
222
|
-
return normalizeInviteRecord(
|
|
221
|
+
return normalizeInviteRecord(created);
|
|
223
222
|
} catch (error) {
|
|
224
223
|
if (!isDuplicateEntryError(error)) {
|
|
225
224
|
throw error;
|
|
@@ -271,8 +270,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
271
270
|
id: row.id
|
|
272
271
|
}
|
|
273
272
|
),
|
|
274
|
-
transaction: options?.trx || null
|
|
275
|
-
simplified: false
|
|
273
|
+
transaction: options?.trx || null
|
|
276
274
|
},
|
|
277
275
|
createJsonRestContext(options?.context || null)
|
|
278
276
|
);
|
|
@@ -298,8 +296,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
298
296
|
id: normalizedInviteId
|
|
299
297
|
}
|
|
300
298
|
),
|
|
301
|
-
transaction: options?.trx || null
|
|
302
|
-
simplified: false
|
|
299
|
+
transaction: options?.trx || null
|
|
303
300
|
},
|
|
304
301
|
createJsonRestContext(options?.context || null)
|
|
305
302
|
);
|
|
@@ -324,8 +321,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
324
321
|
id: normalizedInviteId
|
|
325
322
|
}
|
|
326
323
|
),
|
|
327
|
-
transaction: options?.trx || null
|
|
328
|
-
simplified: false
|
|
324
|
+
transaction: options?.trx || null
|
|
329
325
|
},
|
|
330
326
|
createJsonRestContext(options?.context || null)
|
|
331
327
|
);
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
createJsonApiInputRecord,
|
|
12
12
|
createJsonApiRelationship,
|
|
13
13
|
createJsonRestContext,
|
|
14
|
-
|
|
14
|
+
extractJsonRestCollectionRows
|
|
15
15
|
} from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
|
|
16
16
|
import { OWNER_ROLE_ID } from "../../../shared/roles.js";
|
|
17
17
|
|
|
@@ -84,20 +84,27 @@ function createRepository({ api, knex } = {}) {
|
|
|
84
84
|
|
|
85
85
|
const withTransaction = createWithTransaction(knex);
|
|
86
86
|
|
|
87
|
-
async function queryMemberships(filters = {}, options = {}, {
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
async function queryMemberships(filters = {}, options = {}, { include = [] } = {}) {
|
|
88
|
+
const normalizedInclude = Array.from(
|
|
89
|
+
new Set(
|
|
90
|
+
(Array.isArray(include) ? include : [])
|
|
91
|
+
.map((entry) => normalizeText(entry))
|
|
92
|
+
.filter(Boolean)
|
|
93
|
+
)
|
|
94
|
+
);
|
|
95
|
+
return extractJsonRestCollectionRows(
|
|
96
|
+
await api.resources.workspaceMemberships.query(
|
|
97
|
+
{
|
|
98
|
+
queryParams: {
|
|
99
|
+
filters,
|
|
100
|
+
...(normalizedInclude.length > 0 ? { include: normalizedInclude } : {})
|
|
101
|
+
},
|
|
102
|
+
transaction: options?.trx || null,
|
|
103
|
+
simplified: true
|
|
93
104
|
},
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
},
|
|
97
|
-
createJsonRestContext(options?.context || null)
|
|
105
|
+
createJsonRestContext(options?.context || null)
|
|
106
|
+
)
|
|
98
107
|
);
|
|
99
|
-
|
|
100
|
-
return Array.isArray(simplifyJsonApiDocument(result)) ? simplifyJsonApiDocument(result) : [];
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
async function findByWorkspaceIdAndUserId(workspaceId, userId, options = {}) {
|
|
@@ -141,8 +148,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
141
148
|
id: existing.id
|
|
142
149
|
}
|
|
143
150
|
),
|
|
144
|
-
transaction: options?.trx || null
|
|
145
|
-
simplified: false
|
|
151
|
+
transaction: options?.trx || null
|
|
146
152
|
},
|
|
147
153
|
createJsonRestContext(options?.context || null)
|
|
148
154
|
);
|
|
@@ -168,8 +174,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
168
174
|
})
|
|
169
175
|
}
|
|
170
176
|
),
|
|
171
|
-
transaction: options?.trx || null
|
|
172
|
-
simplified: false
|
|
177
|
+
transaction: options?.trx || null
|
|
173
178
|
},
|
|
174
179
|
createJsonRestContext(options?.context || null)
|
|
175
180
|
);
|
|
@@ -216,8 +221,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
216
221
|
})
|
|
217
222
|
}
|
|
218
223
|
),
|
|
219
|
-
transaction: options?.trx || null
|
|
220
|
-
simplified: false
|
|
224
|
+
transaction: options?.trx || null
|
|
221
225
|
},
|
|
222
226
|
createJsonRestContext(options?.context || null)
|
|
223
227
|
);
|
|
@@ -242,8 +246,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
242
246
|
id: existing.id
|
|
243
247
|
}
|
|
244
248
|
),
|
|
245
|
-
transaction: options?.trx || null
|
|
246
|
-
simplified: false
|
|
249
|
+
transaction: options?.trx || null
|
|
247
250
|
},
|
|
248
251
|
createJsonRestContext(options?.context || null)
|
|
249
252
|
);
|
|
@@ -263,7 +266,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
263
266
|
status: "active"
|
|
264
267
|
},
|
|
265
268
|
options,
|
|
266
|
-
{
|
|
269
|
+
{ include: ["user"] }
|
|
267
270
|
);
|
|
268
271
|
|
|
269
272
|
const members = rows
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
createJsonApiInputRecord,
|
|
10
10
|
createJsonApiRelationship,
|
|
11
11
|
createJsonRestContext,
|
|
12
|
-
|
|
12
|
+
extractJsonRestCollectionRows
|
|
13
13
|
} from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
|
|
14
14
|
|
|
15
15
|
const RESOURCE_TYPE = "workspaces";
|
|
@@ -54,19 +54,20 @@ function createRepository({ api, knex } = {}) {
|
|
|
54
54
|
const withTransaction = createWithTransaction(knex);
|
|
55
55
|
|
|
56
56
|
async function queryFirst(filters = {}, options = {}) {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
const rows = extractJsonRestCollectionRows(
|
|
58
|
+
await api.resources.workspaces.query(
|
|
59
|
+
{
|
|
60
|
+
queryParams: {
|
|
61
|
+
filters
|
|
62
|
+
},
|
|
63
|
+
transaction: options?.trx || null,
|
|
64
|
+
simplified: true
|
|
61
65
|
},
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
},
|
|
65
|
-
createJsonRestContext(options?.context || null)
|
|
66
|
+
createJsonRestContext(options?.context || null)
|
|
67
|
+
)
|
|
66
68
|
);
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
return normalizeWorkspaceRecord(Array.isArray(rows) ? rows[0] || null : null);
|
|
70
|
+
return normalizeWorkspaceRecord(rows[0] || null);
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
async function findById(workspaceId, options = {}) {
|
|
@@ -93,24 +94,24 @@ function createRepository({ api, knex } = {}) {
|
|
|
93
94
|
return null;
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
97
|
+
const rows = extractJsonRestCollectionRows(
|
|
98
|
+
await api.resources.workspaces.query(
|
|
99
|
+
{
|
|
100
|
+
queryParams: {
|
|
101
|
+
filters: {
|
|
102
|
+
owner: normalizedUserId,
|
|
103
|
+
isPersonal: true
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
transaction: options?.trx || null,
|
|
107
|
+
simplified: true
|
|
103
108
|
},
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
},
|
|
107
|
-
createJsonRestContext(options?.context || null)
|
|
109
|
+
createJsonRestContext(options?.context || null)
|
|
110
|
+
)
|
|
108
111
|
);
|
|
109
112
|
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
: [];
|
|
113
|
-
rows.sort((left, right) => {
|
|
113
|
+
const normalizedRows = rows.map((row) => normalizeWorkspaceRecord(row)).filter(Boolean);
|
|
114
|
+
normalizedRows.sort((left, right) => {
|
|
114
115
|
const leftId = Number(left?.id);
|
|
115
116
|
const rightId = Number(right?.id);
|
|
116
117
|
if (Number.isFinite(leftId) && Number.isFinite(rightId)) {
|
|
@@ -118,7 +119,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
118
119
|
}
|
|
119
120
|
return String(left?.id || "").localeCompare(String(right?.id || ""));
|
|
120
121
|
});
|
|
121
|
-
return
|
|
122
|
+
return normalizedRows[0] || null;
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
async function insert(payload = {}, options = {}) {
|
|
@@ -149,13 +150,12 @@ function createRepository({ api, knex } = {}) {
|
|
|
149
150
|
relationships: createWorkspaceRelationships({ ownerUserId })
|
|
150
151
|
}
|
|
151
152
|
),
|
|
152
|
-
transaction: options?.trx || null
|
|
153
|
-
simplified: false
|
|
153
|
+
transaction: options?.trx || null
|
|
154
154
|
},
|
|
155
155
|
createJsonRestContext(options?.context || null)
|
|
156
156
|
);
|
|
157
157
|
|
|
158
|
-
return normalizeWorkspaceRecord(
|
|
158
|
+
return normalizeWorkspaceRecord(created);
|
|
159
159
|
} catch (error) {
|
|
160
160
|
if (!isDuplicateEntryError(error)) {
|
|
161
161
|
throw error;
|
|
@@ -195,13 +195,12 @@ function createRepository({ api, knex } = {}) {
|
|
|
195
195
|
relationships
|
|
196
196
|
}
|
|
197
197
|
),
|
|
198
|
-
transaction: options?.trx || null
|
|
199
|
-
simplified: false
|
|
198
|
+
transaction: options?.trx || null
|
|
200
199
|
},
|
|
201
200
|
createJsonRestContext(options?.context || null)
|
|
202
201
|
);
|
|
203
202
|
|
|
204
|
-
return normalizeWorkspaceRecord(
|
|
203
|
+
return normalizeWorkspaceRecord(updated);
|
|
205
204
|
}
|
|
206
205
|
|
|
207
206
|
async function listForUserId(userId, options = {}) {
|
|
@@ -210,22 +209,23 @@ function createRepository({ api, knex } = {}) {
|
|
|
210
209
|
return [];
|
|
211
210
|
}
|
|
212
211
|
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
212
|
+
const rows = extractJsonRestCollectionRows(
|
|
213
|
+
await api.resources.workspaceMemberships.query(
|
|
214
|
+
{
|
|
215
|
+
queryParams: {
|
|
216
|
+
filters: {
|
|
217
|
+
user: normalizedUserId,
|
|
218
|
+
status: "active"
|
|
219
|
+
},
|
|
220
|
+
include: ["workspace"]
|
|
219
221
|
},
|
|
220
|
-
|
|
222
|
+
transaction: options?.trx || null,
|
|
223
|
+
simplified: true
|
|
221
224
|
},
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
},
|
|
225
|
-
createJsonRestContext(options?.context || null)
|
|
225
|
+
createJsonRestContext(options?.context || null)
|
|
226
|
+
)
|
|
226
227
|
);
|
|
227
228
|
|
|
228
|
-
const rows = Array.isArray(simplifyJsonApiDocument(result)) ? simplifyJsonApiDocument(result) : [];
|
|
229
229
|
const workspaces = rows
|
|
230
230
|
.map((row) => {
|
|
231
231
|
const workspace = normalizeWorkspaceRecord(row?.workspace);
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
createJsonApiInputRecord,
|
|
9
9
|
createJsonRestContext,
|
|
10
|
-
|
|
10
|
+
extractJsonRestCollectionRows
|
|
11
11
|
} from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
|
|
12
12
|
import { resolveWorkspaceThemePalettes } from "../../shared/settings.js";
|
|
13
13
|
|
|
@@ -63,19 +63,20 @@ function createRepository({ api, knex } = {}) {
|
|
|
63
63
|
const withTransaction = createWithTransaction(knex);
|
|
64
64
|
|
|
65
65
|
async function queryFirst(filters = {}, options = {}) {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
const rows = extractJsonRestCollectionRows(
|
|
67
|
+
await api.resources.workspaceSettings.query(
|
|
68
|
+
{
|
|
69
|
+
queryParams: {
|
|
70
|
+
filters
|
|
71
|
+
},
|
|
72
|
+
transaction: options?.trx || null,
|
|
73
|
+
simplified: true
|
|
70
74
|
},
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
},
|
|
74
|
-
createJsonRestContext(options?.context || null)
|
|
75
|
+
createJsonRestContext(options?.context || null)
|
|
76
|
+
)
|
|
75
77
|
);
|
|
76
78
|
|
|
77
|
-
|
|
78
|
-
return Array.isArray(rows) ? rows[0] || null : null;
|
|
79
|
+
return rows[0] || null;
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
async function findByWorkspaceId(workspaceId, options = {}) {
|
|
@@ -108,8 +109,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
108
109
|
id: normalizedWorkspaceId
|
|
109
110
|
}
|
|
110
111
|
),
|
|
111
|
-
transaction: options?.trx || null
|
|
112
|
-
simplified: false
|
|
112
|
+
transaction: options?.trx || null
|
|
113
113
|
},
|
|
114
114
|
createJsonRestContext(options?.context || null)
|
|
115
115
|
);
|
|
@@ -148,8 +148,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
148
148
|
id: normalizedWorkspaceId
|
|
149
149
|
}
|
|
150
150
|
),
|
|
151
|
-
transaction: options?.trx || null
|
|
152
|
-
simplified: false
|
|
151
|
+
transaction: options?.trx || null
|
|
153
152
|
},
|
|
154
153
|
createJsonRestContext(options?.context || null)
|
|
155
154
|
);
|
|
@@ -15,34 +15,34 @@ function createKnexStub() {
|
|
|
15
15
|
return knex;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
function
|
|
18
|
+
function asCollectionDocument(rows = []) {
|
|
19
|
+
return {
|
|
20
|
+
data: Array.isArray(rows) ? rows : []
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function toWorkspaceInviteRow(row = {}) {
|
|
19
25
|
return {
|
|
20
|
-
type: "workspaceInvites",
|
|
21
26
|
id: String(row.id || ""),
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
tokenHash: row.tokenHash,
|
|
27
|
-
expiresAt: row.expiresAt,
|
|
28
|
-
acceptedAt: row.acceptedAt,
|
|
29
|
-
revokedAt: row.revokedAt,
|
|
30
|
-
createdAt: row.createdAt,
|
|
31
|
-
updatedAt: row.updatedAt
|
|
27
|
+
workspaceId: row?.workspace?.id == null ? null : String(row.workspace.id),
|
|
28
|
+
workspace: row?.workspace?.id == null ? null : {
|
|
29
|
+
...row.workspace,
|
|
30
|
+
id: String(row.workspace.id)
|
|
32
31
|
},
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
32
|
+
email: row.email,
|
|
33
|
+
roleSid: row.roleSid,
|
|
34
|
+
status: row.status,
|
|
35
|
+
tokenHash: row.tokenHash,
|
|
36
|
+
invitedByUserId: row?.invitedByUser?.id == null ? null : String(row.invitedByUser.id),
|
|
37
|
+
invitedByUser: row?.invitedByUser?.id == null ? null : {
|
|
38
|
+
...row.invitedByUser,
|
|
39
|
+
id: String(row.invitedByUser.id)
|
|
40
|
+
},
|
|
41
|
+
expiresAt: row.expiresAt,
|
|
42
|
+
acceptedAt: row.acceptedAt,
|
|
43
|
+
revokedAt: row.revokedAt,
|
|
44
|
+
createdAt: row.createdAt,
|
|
45
|
+
updatedAt: row.updatedAt
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -60,7 +60,6 @@ function createWorkspaceInvitesApiStub({
|
|
|
60
60
|
workspaceInvites: {
|
|
61
61
|
async query({ queryParams }) {
|
|
62
62
|
const filters = queryParams?.filters || {};
|
|
63
|
-
const includeWorkspace = Array.isArray(queryParams?.include) && queryParams.include.includes("workspace");
|
|
64
63
|
const matching = rows.filter((row) => {
|
|
65
64
|
if (Object.hasOwn(filters, "id") && String(row.id) !== String(filters.id)) {
|
|
66
65
|
return false;
|
|
@@ -80,25 +79,9 @@ function createWorkspaceInvitesApiStub({
|
|
|
80
79
|
return true;
|
|
81
80
|
});
|
|
82
81
|
|
|
83
|
-
return
|
|
84
|
-
data: matching.map((row) => toWorkspaceInviteResource(row, { includeWorkspace })),
|
|
85
|
-
included: includeWorkspace
|
|
86
|
-
? matching
|
|
87
|
-
.filter((row) => row?.workspace?.id != null)
|
|
88
|
-
.map((row) => ({
|
|
89
|
-
type: "workspaces",
|
|
90
|
-
id: String(row.workspace.id),
|
|
91
|
-
attributes: {
|
|
92
|
-
slug: row.workspace.slug,
|
|
93
|
-
name: row.workspace.name,
|
|
94
|
-
avatarUrl: row.workspace.avatarUrl
|
|
95
|
-
}
|
|
96
|
-
}))
|
|
97
|
-
: []
|
|
98
|
-
};
|
|
82
|
+
return asCollectionDocument(matching.map((row) => toWorkspaceInviteRow(row)));
|
|
99
83
|
},
|
|
100
84
|
async post(payload) {
|
|
101
|
-
assert.equal(payload?.simplified, false);
|
|
102
85
|
const inputRecord = payload?.inputRecord?.data || {};
|
|
103
86
|
state.postPayload = inputRecord;
|
|
104
87
|
const row = {
|
|
@@ -119,10 +102,9 @@ function createWorkspaceInvitesApiStub({
|
|
|
119
102
|
};
|
|
120
103
|
rows.push(row);
|
|
121
104
|
rowById.set("1", row);
|
|
122
|
-
return
|
|
105
|
+
return toWorkspaceInviteRow(row);
|
|
123
106
|
},
|
|
124
107
|
async patch(payload) {
|
|
125
|
-
assert.equal(payload?.simplified, false);
|
|
126
108
|
const inputRecord = payload?.inputRecord?.data || {};
|
|
127
109
|
state.patchPayloads.push(inputRecord);
|
|
128
110
|
const existing = rowById.get(String(inputRecord.id));
|
|
@@ -134,7 +116,7 @@ function createWorkspaceInvitesApiStub({
|
|
|
134
116
|
rowById.set(String(inputRecord.id), updated);
|
|
135
117
|
}
|
|
136
118
|
const updatedRow = rowById.get(String(inputRecord.id)) || null;
|
|
137
|
-
return updatedRow ?
|
|
119
|
+
return updatedRow ? toWorkspaceInviteRow(updatedRow) : null;
|
|
138
120
|
}
|
|
139
121
|
}
|
|
140
122
|
}
|
|
@@ -15,29 +15,31 @@ function createKnexStub() {
|
|
|
15
15
|
return knex;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
function
|
|
18
|
+
function asCollectionDocument(rows = []) {
|
|
19
|
+
return {
|
|
20
|
+
data: Array.isArray(rows) ? rows : []
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function toWorkspaceMembershipRow(row = {}) {
|
|
19
25
|
return {
|
|
20
|
-
type: "workspaceMemberships",
|
|
21
26
|
id: String(row.id || ""),
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
updatedAt: row.updatedAt
|
|
27
|
+
workspaceId: row?.workspace?.id == null ? null : String(row.workspace.id),
|
|
28
|
+
workspace: row?.workspace?.id == null ? null : {
|
|
29
|
+
...row.workspace,
|
|
30
|
+
id: String(row.workspace.id)
|
|
27
31
|
},
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
}
|
|
32
|
+
userId: row?.user?.id == null ? null : String(row.user.id),
|
|
33
|
+
user: row?.user?.id == null
|
|
34
|
+
? null
|
|
35
|
+
: {
|
|
36
|
+
...row.user,
|
|
37
|
+
id: String(row.user.id)
|
|
38
|
+
},
|
|
39
|
+
roleSid: row.roleSid,
|
|
40
|
+
status: row.status,
|
|
41
|
+
createdAt: row.createdAt,
|
|
42
|
+
updatedAt: row.updatedAt
|
|
41
43
|
};
|
|
42
44
|
}
|
|
43
45
|
|
|
@@ -48,35 +50,25 @@ function createWorkspaceMembershipsApiStub({
|
|
|
48
50
|
} = {}) {
|
|
49
51
|
const state = {
|
|
50
52
|
postPayload: null,
|
|
51
|
-
patchPayload: null
|
|
53
|
+
patchPayload: null,
|
|
54
|
+
queryCalls: []
|
|
52
55
|
};
|
|
53
56
|
|
|
54
57
|
const api = {
|
|
55
58
|
resources: {
|
|
56
59
|
workspaceMemberships: {
|
|
57
60
|
async query({ queryParams }) {
|
|
61
|
+
state.queryCalls.push(queryParams || {});
|
|
58
62
|
const filters = queryParams?.filters || {};
|
|
59
63
|
const includeUser = Array.isArray(queryParams?.include) && queryParams.include.includes("user");
|
|
60
64
|
|
|
61
65
|
if (Object.hasOwn(filters, "workspace") && Object.hasOwn(filters, "user")) {
|
|
62
66
|
const row = rowByComposite.get(`${filters.workspace}:${filters.user}`) || null;
|
|
63
|
-
return
|
|
67
|
+
return asCollectionDocument(row ? [toWorkspaceMembershipRow(row)] : []);
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
if (Object.hasOwn(filters, "workspace") && Object.hasOwn(filters, "status") && includeUser) {
|
|
67
|
-
return
|
|
68
|
-
data: memberSummaryRows.map((row) => toWorkspaceMembershipResource(row)),
|
|
69
|
-
included: memberSummaryRows
|
|
70
|
-
.filter((row) => row?.user?.id != null)
|
|
71
|
-
.map((row) => ({
|
|
72
|
-
type: "userProfiles",
|
|
73
|
-
id: String(row.user.id),
|
|
74
|
-
attributes: {
|
|
75
|
-
displayName: row.user.displayName,
|
|
76
|
-
email: row.user.email
|
|
77
|
-
}
|
|
78
|
-
}))
|
|
79
|
-
};
|
|
71
|
+
return asCollectionDocument(memberSummaryRows.map((row) => toWorkspaceMembershipRow(row)));
|
|
80
72
|
}
|
|
81
73
|
|
|
82
74
|
if (Object.hasOwn(filters, "user") && Object.hasOwn(filters, "status")) {
|
|
@@ -84,13 +76,12 @@ function createWorkspaceMembershipsApiStub({
|
|
|
84
76
|
String(row?.user?.id || "") === String(filters.user) &&
|
|
85
77
|
String(row?.status || "") === String(filters.status)
|
|
86
78
|
));
|
|
87
|
-
return
|
|
79
|
+
return asCollectionDocument(rows.map((row) => toWorkspaceMembershipRow(row)));
|
|
88
80
|
}
|
|
89
81
|
|
|
90
|
-
return
|
|
82
|
+
return asCollectionDocument([]);
|
|
91
83
|
},
|
|
92
84
|
async post(payload) {
|
|
93
|
-
assert.equal(payload?.simplified, false);
|
|
94
85
|
const inputRecord = payload?.inputRecord?.data || {};
|
|
95
86
|
state.postPayload = inputRecord;
|
|
96
87
|
const row = rowById.get("1") || {
|
|
@@ -104,10 +95,9 @@ function createWorkspaceMembershipsApiStub({
|
|
|
104
95
|
};
|
|
105
96
|
rowByComposite.set(`${row.workspace.id}:${row.user.id}`, row);
|
|
106
97
|
rowById.set(String(row.id), row);
|
|
107
|
-
return
|
|
98
|
+
return toWorkspaceMembershipRow(row);
|
|
108
99
|
},
|
|
109
100
|
async patch(payload) {
|
|
110
|
-
assert.equal(payload?.simplified, false);
|
|
111
101
|
const inputRecord = payload?.inputRecord?.data || {};
|
|
112
102
|
state.patchPayload = inputRecord;
|
|
113
103
|
const existing = rowById.get(String(inputRecord.id));
|
|
@@ -120,7 +110,7 @@ function createWorkspaceMembershipsApiStub({
|
|
|
120
110
|
};
|
|
121
111
|
rowById.set(String(updated.id), updated);
|
|
122
112
|
rowByComposite.set(`${updated.workspace.id}:${updated.user.id}`, updated);
|
|
123
|
-
return
|
|
113
|
+
return toWorkspaceMembershipRow(updated);
|
|
124
114
|
}
|
|
125
115
|
}
|
|
126
116
|
}
|
|
@@ -250,3 +240,23 @@ test("workspaceMembershipsRepository.listActiveByWorkspaceId keeps summary rows
|
|
|
250
240
|
}
|
|
251
241
|
]);
|
|
252
242
|
});
|
|
243
|
+
|
|
244
|
+
test("workspaceMembershipsRepository.listActiveWorkspaceIdsByUserId returns normalized workspace ids from the canonical resource", async () => {
|
|
245
|
+
const membershipRow = {
|
|
246
|
+
id: "11",
|
|
247
|
+
workspace: { id: "7" },
|
|
248
|
+
user: { id: "9" },
|
|
249
|
+
roleSid: "owner",
|
|
250
|
+
status: "active",
|
|
251
|
+
createdAt: "2026-03-09 00:26:35.710",
|
|
252
|
+
updatedAt: "2026-03-10 00:26:35.710"
|
|
253
|
+
};
|
|
254
|
+
const { api } = createWorkspaceMembershipsApiStub({
|
|
255
|
+
rowByComposite: new Map([["7:9", membershipRow]])
|
|
256
|
+
});
|
|
257
|
+
const repository = createRepository({ api, knex: createKnexStub() });
|
|
258
|
+
|
|
259
|
+
const workspaceIds = await repository.listActiveWorkspaceIdsByUserId("9");
|
|
260
|
+
|
|
261
|
+
assert.deepEqual(workspaceIds, ["7"]);
|
|
262
|
+
});
|
|
@@ -20,6 +20,12 @@ function normalizeWorkspaceColor(value) {
|
|
|
20
20
|
return typeof value === "string" ? value.toUpperCase() : value;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
function asCollectionDocument(rows = []) {
|
|
24
|
+
return {
|
|
25
|
+
data: Array.isArray(rows) ? rows : []
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
23
29
|
function createWorkspaceSettingsApiStub(rowOverrides = {}) {
|
|
24
30
|
const DEFAULT_WORKSPACE_THEME = resolveWorkspaceThemePalettes({});
|
|
25
31
|
const STUB_CREATED_AT = "2026-03-09 00:26:35.710";
|
|
@@ -51,31 +57,15 @@ function createWorkspaceSettingsApiStub(rowOverrides = {}) {
|
|
|
51
57
|
async query({ queryParams }) {
|
|
52
58
|
const id = String(queryParams?.filters?.id || "");
|
|
53
59
|
if (!state.row || (id && String(state.row.id) !== id)) {
|
|
54
|
-
return
|
|
60
|
+
return asCollectionDocument([]);
|
|
55
61
|
}
|
|
56
62
|
|
|
57
|
-
return {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
attributes: {
|
|
62
|
-
lightPrimaryColor: state.row.lightPrimaryColor,
|
|
63
|
-
lightSecondaryColor: state.row.lightSecondaryColor,
|
|
64
|
-
lightSurfaceColor: state.row.lightSurfaceColor,
|
|
65
|
-
lightSurfaceVariantColor: state.row.lightSurfaceVariantColor,
|
|
66
|
-
darkPrimaryColor: state.row.darkPrimaryColor,
|
|
67
|
-
darkSecondaryColor: state.row.darkSecondaryColor,
|
|
68
|
-
darkSurfaceColor: state.row.darkSurfaceColor,
|
|
69
|
-
darkSurfaceVariantColor: state.row.darkSurfaceVariantColor,
|
|
70
|
-
invitesEnabled: state.row.invitesEnabled,
|
|
71
|
-
createdAt: state.row.createdAt,
|
|
72
|
-
updatedAt: state.row.updatedAt
|
|
73
|
-
}
|
|
74
|
-
}]
|
|
75
|
-
};
|
|
63
|
+
return asCollectionDocument([{
|
|
64
|
+
...state.row,
|
|
65
|
+
id: String(state.row.id)
|
|
66
|
+
}]);
|
|
76
67
|
},
|
|
77
68
|
async post(payload) {
|
|
78
|
-
assert.equal(payload?.simplified, false);
|
|
79
69
|
const inputRecord = payload?.inputRecord?.data || {};
|
|
80
70
|
const attributes = inputRecord.attributes || {};
|
|
81
71
|
state.postPayload = inputRecord;
|
|
@@ -94,17 +84,11 @@ function createWorkspaceSettingsApiStub(rowOverrides = {}) {
|
|
|
94
84
|
updatedAt: toIsoString("2026-03-10 00:00:00.000")
|
|
95
85
|
};
|
|
96
86
|
return {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
id: String(state.row.id),
|
|
100
|
-
attributes: {
|
|
101
|
-
...state.row
|
|
102
|
-
}
|
|
103
|
-
}
|
|
87
|
+
...state.row,
|
|
88
|
+
id: String(state.row.id)
|
|
104
89
|
};
|
|
105
90
|
},
|
|
106
91
|
async patch(payload) {
|
|
107
|
-
assert.equal(payload?.simplified, false);
|
|
108
92
|
const inputRecord = payload?.inputRecord?.data || {};
|
|
109
93
|
const attributes = inputRecord.attributes || {};
|
|
110
94
|
state.patchPayload = inputRecord;
|
|
@@ -122,13 +106,8 @@ function createWorkspaceSettingsApiStub(rowOverrides = {}) {
|
|
|
122
106
|
id: String(inputRecord.id || state.row?.id || "")
|
|
123
107
|
};
|
|
124
108
|
return {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
id: String(state.row.id),
|
|
128
|
-
attributes: {
|
|
129
|
-
...state.row
|
|
130
|
-
}
|
|
131
|
-
}
|
|
109
|
+
...state.row,
|
|
110
|
+
id: String(state.row.id)
|
|
132
111
|
};
|
|
133
112
|
}
|
|
134
113
|
}
|
|
@@ -13,50 +13,35 @@ function createKnexStub() {
|
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
function
|
|
16
|
+
function asCollectionDocument(rows = []) {
|
|
17
|
+
return {
|
|
18
|
+
data: Array.isArray(rows) ? rows : []
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function toWorkspaceRow(row = {}) {
|
|
17
23
|
return {
|
|
18
|
-
type: "workspaces",
|
|
19
24
|
id: String(row.id || ""),
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
},
|
|
29
|
-
relationships: {
|
|
30
|
-
owner: {
|
|
31
|
-
data: row.ownerUserId == null
|
|
32
|
-
? null
|
|
33
|
-
: {
|
|
34
|
-
type: "userProfiles",
|
|
35
|
-
id: String(row.ownerUserId)
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
25
|
+
slug: row.slug,
|
|
26
|
+
name: row.name,
|
|
27
|
+
ownerUserId: row.ownerUserId == null ? null : String(row.ownerUserId),
|
|
28
|
+
isPersonal: row.isPersonal,
|
|
29
|
+
avatarUrl: row.avatarUrl,
|
|
30
|
+
createdAt: row.createdAt,
|
|
31
|
+
updatedAt: row.updatedAt,
|
|
32
|
+
deletedAt: row.deletedAt
|
|
39
33
|
};
|
|
40
34
|
}
|
|
41
35
|
|
|
42
|
-
function
|
|
36
|
+
function toWorkspaceMembershipRow(row = {}) {
|
|
43
37
|
return {
|
|
44
|
-
type: "workspaceMemberships",
|
|
45
38
|
id: String(row.id || ""),
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
relationships: {
|
|
53
|
-
user: {
|
|
54
|
-
data: row?.user?.id == null ? null : { type: "userProfiles", id: String(row.user.id) }
|
|
55
|
-
},
|
|
56
|
-
workspace: {
|
|
57
|
-
data: row?.workspace?.id == null ? null : { type: "workspaces", id: String(row.workspace.id) }
|
|
58
|
-
}
|
|
59
|
-
}
|
|
39
|
+
roleSid: row.roleSid,
|
|
40
|
+
status: row.status,
|
|
41
|
+
createdAt: row.createdAt,
|
|
42
|
+
updatedAt: row.updatedAt,
|
|
43
|
+
user: row?.user?.id == null ? null : { ...row.user, id: String(row.user.id) },
|
|
44
|
+
workspace: row?.workspace?.id == null ? null : toWorkspaceRow(row.workspace)
|
|
60
45
|
};
|
|
61
46
|
}
|
|
62
47
|
|
|
@@ -80,23 +65,22 @@ function createWorkspacesApiStub({
|
|
|
80
65
|
|
|
81
66
|
if (Object.hasOwn(filters, "id")) {
|
|
82
67
|
const row = rowsById.get(String(filters.id)) || null;
|
|
83
|
-
return
|
|
68
|
+
return asCollectionDocument(row ? [toWorkspaceRow(row)] : []);
|
|
84
69
|
}
|
|
85
70
|
|
|
86
71
|
if (Object.hasOwn(filters, "slug")) {
|
|
87
72
|
const row = rowsBySlug.get(String(filters.slug)) || null;
|
|
88
|
-
return
|
|
73
|
+
return asCollectionDocument(row ? [toWorkspaceRow(row)] : []);
|
|
89
74
|
}
|
|
90
75
|
|
|
91
76
|
if (Object.hasOwn(filters, "owner") && Object.hasOwn(filters, "isPersonal")) {
|
|
92
77
|
const rows = personalRowsByOwnerId.get(String(filters.owner)) || [];
|
|
93
|
-
return
|
|
78
|
+
return asCollectionDocument(rows.map((row) => toWorkspaceRow(row)));
|
|
94
79
|
}
|
|
95
80
|
|
|
96
|
-
return
|
|
81
|
+
return asCollectionDocument([]);
|
|
97
82
|
},
|
|
98
83
|
async post(payload) {
|
|
99
|
-
assert.equal(payload?.simplified, false);
|
|
100
84
|
const inputRecord = payload?.inputRecord?.data || {};
|
|
101
85
|
state.postPayload = inputRecord;
|
|
102
86
|
if (insertError) {
|
|
@@ -118,10 +102,9 @@ function createWorkspacesApiStub({
|
|
|
118
102
|
if (row.slug) {
|
|
119
103
|
rowsBySlug.set(row.slug, row);
|
|
120
104
|
}
|
|
121
|
-
return
|
|
105
|
+
return toWorkspaceRow(row);
|
|
122
106
|
},
|
|
123
107
|
async patch(payload) {
|
|
124
|
-
assert.equal(payload?.simplified, false);
|
|
125
108
|
const inputRecord = payload?.inputRecord?.data || {};
|
|
126
109
|
state.patchPayload = inputRecord;
|
|
127
110
|
const existing = rowsById.get(String(inputRecord.id)) || {
|
|
@@ -139,29 +122,21 @@ function createWorkspacesApiStub({
|
|
|
139
122
|
if (updated.slug) {
|
|
140
123
|
rowsBySlug.set(String(updated.slug), updated);
|
|
141
124
|
}
|
|
142
|
-
return
|
|
125
|
+
return toWorkspaceRow(updated);
|
|
143
126
|
}
|
|
144
127
|
},
|
|
145
128
|
workspaceMemberships: {
|
|
146
129
|
async query({ queryParams }) {
|
|
147
130
|
const filters = queryParams?.filters || {};
|
|
148
|
-
const includeWorkspace = Array.isArray(queryParams?.include) && queryParams.include.includes("workspace");
|
|
149
131
|
if (Object.hasOwn(filters, "user") && Object.hasOwn(filters, "status")) {
|
|
150
132
|
const rows = membershipRows.filter((row) => (
|
|
151
133
|
String(row?.user?.id || "") === String(filters.user) &&
|
|
152
134
|
String(row?.status || "") === String(filters.status)
|
|
153
135
|
));
|
|
154
|
-
return
|
|
155
|
-
data: rows.map((row) => toWorkspaceMembershipResource(row)),
|
|
156
|
-
included: includeWorkspace
|
|
157
|
-
? rows
|
|
158
|
-
.filter((row) => row?.workspace?.id != null)
|
|
159
|
-
.map((row) => toWorkspaceResource(row.workspace))
|
|
160
|
-
: []
|
|
161
|
-
};
|
|
136
|
+
return asCollectionDocument(rows.map((row) => toWorkspaceMembershipRow(row)));
|
|
162
137
|
}
|
|
163
138
|
|
|
164
|
-
return
|
|
139
|
+
return asCollectionDocument([]);
|
|
165
140
|
}
|
|
166
141
|
}
|
|
167
142
|
}
|