@jskit-ai/workspaces-core 0.1.163 → 0.1.165
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.json +9 -9
- package/src/server/WorkspacesFeature.js +6 -14
- package/src/server/common/repositories/repositoryUtils.js +2 -4
- package/src/server/common/repositories/workspaceInvitesRepository.js +42 -77
- package/src/server/common/repositories/workspaceMembershipsRepository.js +41 -78
- package/src/server/common/repositories/workspacesRepository.js +22 -45
- package/src/server/workspaceSettings/workspaceSettingsRepository.js +15 -28
- package/test/featureRuntime.test.js +0 -1
- package/test/repositories.integration.test.js +323 -0
- package/test/repositoryContracts.test.js +43 -19
- package/test/workspaceInvitesRepository.test.js +41 -44
- package/test/workspaceMembershipsRepository.test.js +34 -37
- package/test/workspaceSettingsRepository.test.js +51 -61
- package/test/workspacesRepository.test.js +46 -46
|
@@ -3,18 +3,6 @@ import test from "node:test";
|
|
|
3
3
|
import { toIsoString } from "@jskit-ai/database-runtime/shared";
|
|
4
4
|
import { createRepository } from "../src/server/common/repositories/workspaceMembershipsRepository.js";
|
|
5
5
|
|
|
6
|
-
function createKnexStub() {
|
|
7
|
-
const knex = Object.assign(() => {
|
|
8
|
-
throw new Error("query execution not expected");
|
|
9
|
-
}, {
|
|
10
|
-
async transaction(work) {
|
|
11
|
-
return work({ trxId: "trx-1" });
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
return knex;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
6
|
function asCollectionDocument(rows = []) {
|
|
19
7
|
return {
|
|
20
8
|
data: Array.isArray(rows) ? rows : []
|
|
@@ -55,7 +43,8 @@ function createWorkspaceMembershipsApiStub({
|
|
|
55
43
|
const api = {
|
|
56
44
|
resources: {
|
|
57
45
|
workspaceMemberships: {
|
|
58
|
-
async query({ queryParams }) {
|
|
46
|
+
async query({ queryParams, format }) {
|
|
47
|
+
assert.equal(format, "plain");
|
|
59
48
|
state.queryCalls.push(queryParams || {});
|
|
60
49
|
const filters = queryParams?.filters || {};
|
|
61
50
|
const includeUser = Array.isArray(queryParams?.include) && queryParams.include.includes("user");
|
|
@@ -100,14 +89,18 @@ function createWorkspaceMembershipsApiStub({
|
|
|
100
89
|
return asCollectionDocument([]);
|
|
101
90
|
},
|
|
102
91
|
async post(payload) {
|
|
103
|
-
const
|
|
104
|
-
|
|
92
|
+
const data = payload?.data || {};
|
|
93
|
+
assert.equal(payload.format, "plain");
|
|
94
|
+
assert.equal(payload.returning, "full");
|
|
95
|
+
assert.equal(payload.inputRecord, undefined);
|
|
96
|
+
assert.equal(payload.document, undefined);
|
|
97
|
+
state.postPayload = payload;
|
|
105
98
|
const row = rowById.get("1") || {
|
|
106
99
|
id: "1",
|
|
107
|
-
workspace: { id: String(
|
|
108
|
-
user: { id: String(
|
|
109
|
-
roleSid: String(
|
|
110
|
-
status: String(
|
|
100
|
+
workspace: { id: String(data.workspace || "") },
|
|
101
|
+
user: { id: String(data.user || "") },
|
|
102
|
+
roleSid: String(data.roleSid || ""),
|
|
103
|
+
status: String(data.status || ""),
|
|
111
104
|
createdAt: "2026-03-09 00:26:35.710",
|
|
112
105
|
updatedAt: "2026-03-09 00:26:35.710"
|
|
113
106
|
};
|
|
@@ -116,13 +109,17 @@ function createWorkspaceMembershipsApiStub({
|
|
|
116
109
|
return toWorkspaceMembershipRow(row);
|
|
117
110
|
},
|
|
118
111
|
async patch(payload) {
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
112
|
+
const data = payload?.data || {};
|
|
113
|
+
assert.equal(payload.format, "plain");
|
|
114
|
+
assert.equal(payload.returning, "full");
|
|
115
|
+
assert.equal(payload.inputRecord, undefined);
|
|
116
|
+
assert.equal(payload.document, undefined);
|
|
117
|
+
state.patchPayload = payload;
|
|
118
|
+
const existing = rowById.get(String(payload.id));
|
|
122
119
|
const updated = {
|
|
123
120
|
...(existing || {}),
|
|
124
|
-
...
|
|
125
|
-
id: String(
|
|
121
|
+
...data,
|
|
122
|
+
id: String(payload.id),
|
|
126
123
|
workspace: existing?.workspace || { id: "" },
|
|
127
124
|
user: existing?.user || { id: "" }
|
|
128
125
|
};
|
|
@@ -150,7 +147,7 @@ test("workspaceMembershipsRepository.findByWorkspaceIdAndUserId normalizes canon
|
|
|
150
147
|
const { api } = createWorkspaceMembershipsApiStub({
|
|
151
148
|
rowByComposite: new Map([["7:9", membershipRow]])
|
|
152
149
|
});
|
|
153
|
-
const repository = createRepository({ api
|
|
150
|
+
const repository = createRepository({ api });
|
|
154
151
|
|
|
155
152
|
const membership = await repository.findByWorkspaceIdAndUserId("7", "9");
|
|
156
153
|
|
|
@@ -185,13 +182,13 @@ test("workspaceMembershipsRepository.ensureOwnerMembership upgrades an existing
|
|
|
185
182
|
rowById: new Map([["11", refreshedRow]]),
|
|
186
183
|
rowByComposite: new Map([["7:9", existingRow]])
|
|
187
184
|
});
|
|
188
|
-
const repository = createRepository({ api
|
|
185
|
+
const repository = createRepository({ api });
|
|
189
186
|
|
|
190
187
|
const membership = await repository.ensureOwnerMembership("7", "9");
|
|
191
188
|
|
|
192
|
-
assert.equal(state.patchPayload.
|
|
193
|
-
assert.equal(state.patchPayload.
|
|
194
|
-
assert.equal(typeof state.patchPayload.
|
|
189
|
+
assert.equal(state.patchPayload.data.roleSid, "owner");
|
|
190
|
+
assert.equal(state.patchPayload.data.status, "active");
|
|
191
|
+
assert.equal(typeof state.patchPayload.data.updatedAt, "string");
|
|
195
192
|
assert.deepEqual(membership, {
|
|
196
193
|
id: "11",
|
|
197
194
|
workspaceId: "7",
|
|
@@ -199,7 +196,7 @@ test("workspaceMembershipsRepository.ensureOwnerMembership upgrades an existing
|
|
|
199
196
|
roleSid: "owner",
|
|
200
197
|
status: "active",
|
|
201
198
|
createdAt: toIsoString("2026-03-09 00:26:35.710"),
|
|
202
|
-
updatedAt: toIsoString(state.patchPayload.
|
|
199
|
+
updatedAt: toIsoString(state.patchPayload.data.updatedAt)
|
|
203
200
|
});
|
|
204
201
|
});
|
|
205
202
|
|
|
@@ -217,17 +214,17 @@ test("workspaceMembershipsRepository.upsertMembership creates normalized members
|
|
|
217
214
|
rowById: new Map([["1", createdRow]]),
|
|
218
215
|
rowByComposite: new Map()
|
|
219
216
|
});
|
|
220
|
-
const repository = createRepository({ api
|
|
217
|
+
const repository = createRepository({ api });
|
|
221
218
|
|
|
222
219
|
await repository.upsertMembership("7", "9", {
|
|
223
220
|
roleSid: "ADMIN",
|
|
224
221
|
status: "ACTIVE"
|
|
225
222
|
});
|
|
226
223
|
|
|
227
|
-
assert.equal(state.postPayload.
|
|
228
|
-
assert.equal(state.postPayload.
|
|
229
|
-
assert.equal(state.postPayload.
|
|
230
|
-
assert.equal(state.postPayload.
|
|
224
|
+
assert.equal(state.postPayload.data.workspace, "7");
|
|
225
|
+
assert.equal(state.postPayload.data.user, "9");
|
|
226
|
+
assert.equal(state.postPayload.data.roleSid, "admin");
|
|
227
|
+
assert.equal(state.postPayload.data.status, "active");
|
|
231
228
|
});
|
|
232
229
|
|
|
233
230
|
test("workspaceMembershipsRepository.listActiveByWorkspaceId keeps summary rows separate from the canonical membership resource", async () => {
|
|
@@ -244,7 +241,7 @@ test("workspaceMembershipsRepository.listActiveByWorkspaceId keeps summary rows
|
|
|
244
241
|
}
|
|
245
242
|
]
|
|
246
243
|
});
|
|
247
|
-
const repository = createRepository({ api
|
|
244
|
+
const repository = createRepository({ api });
|
|
248
245
|
|
|
249
246
|
const members = await repository.listActiveByWorkspaceId("7");
|
|
250
247
|
|
|
@@ -272,7 +269,7 @@ test("workspaceMembershipsRepository.listActiveWorkspaceIdsByUserId returns norm
|
|
|
272
269
|
const { api, state } = createWorkspaceMembershipsApiStub({
|
|
273
270
|
rowByComposite: new Map([["7:9", membershipRow]])
|
|
274
271
|
});
|
|
275
|
-
const repository = createRepository({ api
|
|
272
|
+
const repository = createRepository({ api });
|
|
276
273
|
|
|
277
274
|
const workspaceIds = await repository.listActiveWorkspaceIdsByUserId("9");
|
|
278
275
|
|
|
@@ -4,18 +4,6 @@ import { toIsoString } from "@jskit-ai/database-runtime/shared";
|
|
|
4
4
|
import { resolveWorkspaceThemePalettes } from "@jskit-ai/workspaces-core/shared/settings";
|
|
5
5
|
import { createRepository } from "../src/server/workspaceSettings/workspaceSettingsRepository.js";
|
|
6
6
|
|
|
7
|
-
function createKnexStub() {
|
|
8
|
-
const knex = Object.assign(() => {
|
|
9
|
-
throw new Error("query execution not expected");
|
|
10
|
-
}, {
|
|
11
|
-
async transaction(work) {
|
|
12
|
-
return work({ trxId: "trx-1" });
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
return knex;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
7
|
function normalizeWorkspaceColor(value) {
|
|
20
8
|
return typeof value === "string" ? value.toUpperCase() : value;
|
|
21
9
|
}
|
|
@@ -54,7 +42,8 @@ function createWorkspaceSettingsApiStub(rowOverrides = {}) {
|
|
|
54
42
|
const api = {
|
|
55
43
|
resources: {
|
|
56
44
|
workspaceSettings: {
|
|
57
|
-
async query({ queryParams }) {
|
|
45
|
+
async query({ queryParams, format }) {
|
|
46
|
+
assert.equal(format, "plain");
|
|
58
47
|
const id = String(queryParams?.filters?.id || "");
|
|
59
48
|
if (!state.row || (id && String(state.row.id) !== id)) {
|
|
60
49
|
return asCollectionDocument([]);
|
|
@@ -66,20 +55,23 @@ function createWorkspaceSettingsApiStub(rowOverrides = {}) {
|
|
|
66
55
|
}]);
|
|
67
56
|
},
|
|
68
57
|
async post(payload) {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
58
|
+
const data = payload?.data || {};
|
|
59
|
+
assert.equal(payload.format, "plain");
|
|
60
|
+
assert.equal(payload.returning, "full");
|
|
61
|
+
assert.equal(payload.inputRecord, undefined);
|
|
62
|
+
assert.equal(payload.document, undefined);
|
|
63
|
+
state.postPayload = payload;
|
|
72
64
|
state.row = {
|
|
73
|
-
id: String(
|
|
74
|
-
lightPrimaryColor: normalizeWorkspaceColor(
|
|
75
|
-
lightSecondaryColor: normalizeWorkspaceColor(
|
|
76
|
-
lightSurfaceColor: normalizeWorkspaceColor(
|
|
77
|
-
lightSurfaceVariantColor: normalizeWorkspaceColor(
|
|
78
|
-
darkPrimaryColor: normalizeWorkspaceColor(
|
|
79
|
-
darkSecondaryColor: normalizeWorkspaceColor(
|
|
80
|
-
darkSurfaceColor: normalizeWorkspaceColor(
|
|
81
|
-
darkSurfaceVariantColor: normalizeWorkspaceColor(
|
|
82
|
-
invitesEnabled:
|
|
65
|
+
id: String(data.id),
|
|
66
|
+
lightPrimaryColor: normalizeWorkspaceColor(data.lightPrimaryColor ?? DEFAULT_WORKSPACE_THEME.light.color),
|
|
67
|
+
lightSecondaryColor: normalizeWorkspaceColor(data.lightSecondaryColor ?? DEFAULT_WORKSPACE_THEME.light.secondaryColor),
|
|
68
|
+
lightSurfaceColor: normalizeWorkspaceColor(data.lightSurfaceColor ?? DEFAULT_WORKSPACE_THEME.light.surfaceColor),
|
|
69
|
+
lightSurfaceVariantColor: normalizeWorkspaceColor(data.lightSurfaceVariantColor ?? DEFAULT_WORKSPACE_THEME.light.surfaceVariantColor),
|
|
70
|
+
darkPrimaryColor: normalizeWorkspaceColor(data.darkPrimaryColor ?? DEFAULT_WORKSPACE_THEME.dark.color),
|
|
71
|
+
darkSecondaryColor: normalizeWorkspaceColor(data.darkSecondaryColor ?? DEFAULT_WORKSPACE_THEME.dark.secondaryColor),
|
|
72
|
+
darkSurfaceColor: normalizeWorkspaceColor(data.darkSurfaceColor ?? DEFAULT_WORKSPACE_THEME.dark.surfaceColor),
|
|
73
|
+
darkSurfaceVariantColor: normalizeWorkspaceColor(data.darkSurfaceVariantColor ?? DEFAULT_WORKSPACE_THEME.dark.surfaceVariantColor),
|
|
74
|
+
invitesEnabled: data.invitesEnabled ?? true,
|
|
83
75
|
createdAt: toIsoString("2026-03-10 00:00:00.000"),
|
|
84
76
|
updatedAt: toIsoString("2026-03-10 00:00:00.000")
|
|
85
77
|
};
|
|
@@ -89,21 +81,24 @@ function createWorkspaceSettingsApiStub(rowOverrides = {}) {
|
|
|
89
81
|
};
|
|
90
82
|
},
|
|
91
83
|
async patch(payload) {
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
84
|
+
const data = payload?.data || {};
|
|
85
|
+
assert.equal(payload.format, "plain");
|
|
86
|
+
assert.equal(payload.returning, "full");
|
|
87
|
+
assert.equal(payload.inputRecord, undefined);
|
|
88
|
+
assert.equal(payload.document, undefined);
|
|
89
|
+
state.patchPayload = payload;
|
|
95
90
|
state.row = {
|
|
96
91
|
...state.row,
|
|
97
|
-
...
|
|
98
|
-
...(Object.hasOwn(
|
|
99
|
-
...(Object.hasOwn(
|
|
100
|
-
...(Object.hasOwn(
|
|
101
|
-
...(Object.hasOwn(
|
|
102
|
-
...(Object.hasOwn(
|
|
103
|
-
...(Object.hasOwn(
|
|
104
|
-
...(Object.hasOwn(
|
|
105
|
-
...(Object.hasOwn(
|
|
106
|
-
id: String(
|
|
92
|
+
...data,
|
|
93
|
+
...(Object.hasOwn(data, "lightPrimaryColor") ? { lightPrimaryColor: normalizeWorkspaceColor(data.lightPrimaryColor) } : {}),
|
|
94
|
+
...(Object.hasOwn(data, "lightSecondaryColor") ? { lightSecondaryColor: normalizeWorkspaceColor(data.lightSecondaryColor) } : {}),
|
|
95
|
+
...(Object.hasOwn(data, "lightSurfaceColor") ? { lightSurfaceColor: normalizeWorkspaceColor(data.lightSurfaceColor) } : {}),
|
|
96
|
+
...(Object.hasOwn(data, "lightSurfaceVariantColor") ? { lightSurfaceVariantColor: normalizeWorkspaceColor(data.lightSurfaceVariantColor) } : {}),
|
|
97
|
+
...(Object.hasOwn(data, "darkPrimaryColor") ? { darkPrimaryColor: normalizeWorkspaceColor(data.darkPrimaryColor) } : {}),
|
|
98
|
+
...(Object.hasOwn(data, "darkSecondaryColor") ? { darkSecondaryColor: normalizeWorkspaceColor(data.darkSecondaryColor) } : {}),
|
|
99
|
+
...(Object.hasOwn(data, "darkSurfaceColor") ? { darkSurfaceColor: normalizeWorkspaceColor(data.darkSurfaceColor) } : {}),
|
|
100
|
+
...(Object.hasOwn(data, "darkSurfaceVariantColor") ? { darkSurfaceVariantColor: normalizeWorkspaceColor(data.darkSurfaceVariantColor) } : {}),
|
|
101
|
+
id: String(payload.id || state.row?.id || "")
|
|
107
102
|
};
|
|
108
103
|
return {
|
|
109
104
|
...state.row,
|
|
@@ -120,8 +115,7 @@ function createWorkspaceSettingsApiStub(rowOverrides = {}) {
|
|
|
120
115
|
test("workspaceSettingsRepository.findByWorkspaceId returns the canonical workspace-settings row", async () => {
|
|
121
116
|
const { api, DEFAULT_WORKSPACE_THEME, STUB_CREATED_AT } = createWorkspaceSettingsApiStub();
|
|
122
117
|
const repository = createRepository({
|
|
123
|
-
api
|
|
124
|
-
knex: createKnexStub()
|
|
118
|
+
api
|
|
125
119
|
});
|
|
126
120
|
|
|
127
121
|
const record = await repository.findByWorkspaceId("1");
|
|
@@ -145,15 +139,14 @@ test("workspaceSettingsRepository.findByWorkspaceId returns the canonical worksp
|
|
|
145
139
|
test("workspaceSettingsRepository.updateSettingsByWorkspaceId updates invitesEnabled only", async () => {
|
|
146
140
|
const { api, state } = createWorkspaceSettingsApiStub();
|
|
147
141
|
const repository = createRepository({
|
|
148
|
-
api
|
|
149
|
-
knex: createKnexStub()
|
|
142
|
+
api
|
|
150
143
|
});
|
|
151
144
|
|
|
152
145
|
const updated = await repository.updateSettingsByWorkspaceId("1", {
|
|
153
146
|
invitesEnabled: false
|
|
154
147
|
});
|
|
155
148
|
|
|
156
|
-
assert.equal(state.patchPayload.
|
|
149
|
+
assert.equal(state.patchPayload.data.invitesEnabled, false);
|
|
157
150
|
assert.equal(updated.invitesEnabled, false);
|
|
158
151
|
});
|
|
159
152
|
|
|
@@ -161,22 +154,21 @@ test("workspaceSettingsRepository.ensureForWorkspaceId delegates defaults to the
|
|
|
161
154
|
const { api, state, DEFAULT_WORKSPACE_THEME } = createWorkspaceSettingsApiStub();
|
|
162
155
|
state.row = null;
|
|
163
156
|
const repository = createRepository({
|
|
164
|
-
api
|
|
165
|
-
knex: createKnexStub()
|
|
157
|
+
api
|
|
166
158
|
});
|
|
167
159
|
|
|
168
160
|
const record = await repository.ensureForWorkspaceId("5");
|
|
169
161
|
|
|
170
|
-
assert.equal(state.postPayload.id, "5");
|
|
171
|
-
assert.equal(state.postPayload.
|
|
172
|
-
assert.equal(state.postPayload.
|
|
173
|
-
assert.equal(state.postPayload.
|
|
174
|
-
assert.equal(state.postPayload.
|
|
175
|
-
assert.equal(state.postPayload.
|
|
176
|
-
assert.equal(state.postPayload.
|
|
177
|
-
assert.equal(state.postPayload.
|
|
178
|
-
assert.equal(state.postPayload.
|
|
179
|
-
assert.equal(state.postPayload.
|
|
162
|
+
assert.equal(state.postPayload.data.id, "5");
|
|
163
|
+
assert.equal(state.postPayload.data.lightPrimaryColor, DEFAULT_WORKSPACE_THEME.light.color);
|
|
164
|
+
assert.equal(state.postPayload.data.lightSecondaryColor, DEFAULT_WORKSPACE_THEME.light.secondaryColor);
|
|
165
|
+
assert.equal(state.postPayload.data.lightSurfaceColor, DEFAULT_WORKSPACE_THEME.light.surfaceColor);
|
|
166
|
+
assert.equal(state.postPayload.data.lightSurfaceVariantColor, DEFAULT_WORKSPACE_THEME.light.surfaceVariantColor);
|
|
167
|
+
assert.equal(state.postPayload.data.darkPrimaryColor, DEFAULT_WORKSPACE_THEME.dark.color);
|
|
168
|
+
assert.equal(state.postPayload.data.darkSecondaryColor, DEFAULT_WORKSPACE_THEME.dark.secondaryColor);
|
|
169
|
+
assert.equal(state.postPayload.data.darkSurfaceColor, DEFAULT_WORKSPACE_THEME.dark.surfaceColor);
|
|
170
|
+
assert.equal(state.postPayload.data.darkSurfaceVariantColor, DEFAULT_WORKSPACE_THEME.dark.surfaceVariantColor);
|
|
171
|
+
assert.equal(state.postPayload.data.invitesEnabled, true);
|
|
180
172
|
assert.equal(record.lightPrimaryColor, DEFAULT_WORKSPACE_THEME.light.color);
|
|
181
173
|
assert.equal(record.lightSecondaryColor, DEFAULT_WORKSPACE_THEME.light.secondaryColor);
|
|
182
174
|
assert.equal(record.lightSurfaceColor, DEFAULT_WORKSPACE_THEME.light.surfaceColor);
|
|
@@ -192,15 +184,14 @@ test("workspaceSettingsRepository.ensureForWorkspaceId delegates defaults to the
|
|
|
192
184
|
test("workspaceSettingsRepository.updateSettingsByWorkspaceId updates workspace settings fields", async () => {
|
|
193
185
|
const { api, state } = createWorkspaceSettingsApiStub();
|
|
194
186
|
const repository = createRepository({
|
|
195
|
-
api
|
|
196
|
-
knex: createKnexStub()
|
|
187
|
+
api
|
|
197
188
|
});
|
|
198
189
|
|
|
199
190
|
const updated = await repository.updateSettingsByWorkspaceId("1", {
|
|
200
191
|
lightPrimaryColor: "#123abc"
|
|
201
192
|
});
|
|
202
193
|
|
|
203
|
-
assert.equal(state.patchPayload.
|
|
194
|
+
assert.equal(state.patchPayload.data.lightPrimaryColor, "#123abc");
|
|
204
195
|
assert.equal(updated.lightPrimaryColor, "#123ABC");
|
|
205
196
|
});
|
|
206
197
|
|
|
@@ -208,8 +199,7 @@ test("workspaceSettingsRepository can be constructed without validating app conf
|
|
|
208
199
|
const { api } = createWorkspaceSettingsApiStub();
|
|
209
200
|
|
|
210
201
|
const repository = createRepository({
|
|
211
|
-
api
|
|
212
|
-
knex: createKnexStub()
|
|
202
|
+
api
|
|
213
203
|
});
|
|
214
204
|
|
|
215
205
|
assert.ok(repository);
|
|
@@ -3,16 +3,6 @@ import test from "node:test";
|
|
|
3
3
|
import { toIsoString } from "@jskit-ai/database-runtime/shared";
|
|
4
4
|
import { createRepository } from "../src/server/common/repositories/workspacesRepository.js";
|
|
5
5
|
|
|
6
|
-
function createKnexStub() {
|
|
7
|
-
return Object.assign(() => {
|
|
8
|
-
throw new Error("query execution not expected");
|
|
9
|
-
}, {
|
|
10
|
-
async transaction(work) {
|
|
11
|
-
return work({ trxId: "trx-1" });
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
6
|
function asCollectionDocument(rows = []) {
|
|
17
7
|
return {
|
|
18
8
|
data: Array.isArray(rows) ? rows : []
|
|
@@ -24,7 +14,7 @@ function toWorkspaceRow(row = {}) {
|
|
|
24
14
|
id: String(row.id || ""),
|
|
25
15
|
slug: row.slug,
|
|
26
16
|
name: row.name,
|
|
27
|
-
|
|
17
|
+
owner: row.ownerUserId == null ? null : { id: String(row.ownerUserId) },
|
|
28
18
|
isPersonal: row.isPersonal,
|
|
29
19
|
avatarUrl: row.avatarUrl,
|
|
30
20
|
createdAt: row.createdAt,
|
|
@@ -60,7 +50,8 @@ function createWorkspacesApiStub({
|
|
|
60
50
|
const api = {
|
|
61
51
|
resources: {
|
|
62
52
|
workspaces: {
|
|
63
|
-
async query({ queryParams }) {
|
|
53
|
+
async query({ queryParams, format }) {
|
|
54
|
+
assert.equal(format, "plain");
|
|
64
55
|
const filters = queryParams?.filters || {};
|
|
65
56
|
|
|
66
57
|
if (Object.hasOwn(filters, "id")) {
|
|
@@ -81,21 +72,25 @@ function createWorkspacesApiStub({
|
|
|
81
72
|
return asCollectionDocument([]);
|
|
82
73
|
},
|
|
83
74
|
async post(payload) {
|
|
84
|
-
const
|
|
85
|
-
|
|
75
|
+
const data = payload?.data || {};
|
|
76
|
+
assert.equal(payload.format, "plain");
|
|
77
|
+
assert.equal(payload.returning, "full");
|
|
78
|
+
assert.equal(payload.inputRecord, undefined);
|
|
79
|
+
assert.equal(payload.document, undefined);
|
|
80
|
+
state.postPayload = payload;
|
|
86
81
|
if (insertError) {
|
|
87
82
|
throw insertError;
|
|
88
83
|
}
|
|
89
84
|
|
|
90
85
|
const row = {
|
|
91
86
|
id: "1",
|
|
92
|
-
slug: String(
|
|
93
|
-
name: String(
|
|
94
|
-
ownerUserId: String(
|
|
95
|
-
isPersonal: Boolean(
|
|
96
|
-
avatarUrl: String(
|
|
97
|
-
createdAt:
|
|
98
|
-
updatedAt:
|
|
87
|
+
slug: String(data.slug || ""),
|
|
88
|
+
name: String(data.name || ""),
|
|
89
|
+
ownerUserId: String(data.owner || ""),
|
|
90
|
+
isPersonal: Boolean(data.isPersonal),
|
|
91
|
+
avatarUrl: String(data.avatarUrl || ""),
|
|
92
|
+
createdAt: data.createdAt,
|
|
93
|
+
updatedAt: data.updatedAt,
|
|
99
94
|
deletedAt: null
|
|
100
95
|
};
|
|
101
96
|
rowsById.set(row.id, row);
|
|
@@ -105,18 +100,22 @@ function createWorkspacesApiStub({
|
|
|
105
100
|
return toWorkspaceRow(row);
|
|
106
101
|
},
|
|
107
102
|
async patch(payload) {
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
103
|
+
const data = payload?.data || {};
|
|
104
|
+
assert.equal(payload.format, "plain");
|
|
105
|
+
assert.equal(payload.returning, "full");
|
|
106
|
+
assert.equal(payload.inputRecord, undefined);
|
|
107
|
+
assert.equal(payload.document, undefined);
|
|
108
|
+
state.patchPayload = payload;
|
|
109
|
+
const existing = rowsById.get(String(payload.id)) || {
|
|
110
|
+
id: String(payload.id)
|
|
112
111
|
};
|
|
113
112
|
const updated = {
|
|
114
113
|
...existing,
|
|
115
|
-
...
|
|
116
|
-
...(
|
|
117
|
-
? { ownerUserId: String(
|
|
114
|
+
...data,
|
|
115
|
+
...(data.owner
|
|
116
|
+
? { ownerUserId: String(data.owner) }
|
|
118
117
|
: {}),
|
|
119
|
-
id: String(
|
|
118
|
+
id: String(payload.id)
|
|
120
119
|
};
|
|
121
120
|
rowsById.set(updated.id, updated);
|
|
122
121
|
if (updated.slug) {
|
|
@@ -126,7 +125,8 @@ function createWorkspacesApiStub({
|
|
|
126
125
|
}
|
|
127
126
|
},
|
|
128
127
|
workspaceMemberships: {
|
|
129
|
-
async query({ queryParams }) {
|
|
128
|
+
async query({ queryParams, format }) {
|
|
129
|
+
assert.equal(format, "plain");
|
|
130
130
|
const filters = queryParams?.filters || {};
|
|
131
131
|
if (Object.hasOwn(filters, "user") && Object.hasOwn(filters, "status")) {
|
|
132
132
|
const rows = membershipRows.filter((row) => (
|
|
@@ -161,7 +161,7 @@ test("workspacesRepository.findById reads a canonical workspace row through json
|
|
|
161
161
|
}]
|
|
162
162
|
])
|
|
163
163
|
});
|
|
164
|
-
const repository = createRepository({ api
|
|
164
|
+
const repository = createRepository({ api });
|
|
165
165
|
|
|
166
166
|
const workspace = await repository.findById("7");
|
|
167
167
|
|
|
@@ -207,7 +207,7 @@ test("workspacesRepository.findPersonalByOwnerUserId returns the first personal
|
|
|
207
207
|
]]
|
|
208
208
|
])
|
|
209
209
|
});
|
|
210
|
-
const repository = createRepository({ api
|
|
210
|
+
const repository = createRepository({ api });
|
|
211
211
|
|
|
212
212
|
const workspace = await repository.findPersonalByOwnerUserId("9");
|
|
213
213
|
|
|
@@ -217,7 +217,7 @@ test("workspacesRepository.findPersonalByOwnerUserId returns the first personal
|
|
|
217
217
|
|
|
218
218
|
test("workspacesRepository.insert writes canonical fields through json-rest-api", async () => {
|
|
219
219
|
const { api, state } = createWorkspacesApiStub();
|
|
220
|
-
const repository = createRepository({ api
|
|
220
|
+
const repository = createRepository({ api });
|
|
221
221
|
|
|
222
222
|
const inserted = await repository.insert({
|
|
223
223
|
slug: "TonyMobily3",
|
|
@@ -227,13 +227,13 @@ test("workspacesRepository.insert writes canonical fields through json-rest-api"
|
|
|
227
227
|
isPersonal: false
|
|
228
228
|
});
|
|
229
229
|
|
|
230
|
-
assert.equal(state.postPayload.
|
|
231
|
-
assert.equal(state.postPayload.
|
|
232
|
-
assert.equal(state.postPayload.
|
|
233
|
-
assert.equal(state.postPayload.
|
|
234
|
-
assert.equal(state.postPayload.
|
|
235
|
-
assert.equal(typeof state.postPayload.
|
|
236
|
-
assert.equal(state.postPayload.
|
|
230
|
+
assert.equal(state.postPayload.data.owner, "9");
|
|
231
|
+
assert.equal(state.postPayload.data.slug, "TonyMobily3");
|
|
232
|
+
assert.equal(state.postPayload.data.name, "TonyMobily3");
|
|
233
|
+
assert.equal(state.postPayload.data.isPersonal, false);
|
|
234
|
+
assert.equal(state.postPayload.data.avatarUrl, "");
|
|
235
|
+
assert.equal(typeof state.postPayload.data.createdAt, "string");
|
|
236
|
+
assert.equal(state.postPayload.data.updatedAt, state.postPayload.data.createdAt);
|
|
237
237
|
assert.equal(inserted.id, "1");
|
|
238
238
|
assert.equal(inserted.ownerUserId, "9");
|
|
239
239
|
});
|
|
@@ -252,9 +252,9 @@ test("workspacesRepository.insert falls back to slug lookup on duplicate slug",
|
|
|
252
252
|
};
|
|
253
253
|
const { api } = createWorkspacesApiStub({
|
|
254
254
|
rowsBySlug: new Map([["shared-workspace", existingRow]]),
|
|
255
|
-
insertError: { code: "ER_DUP_ENTRY" }
|
|
255
|
+
insertError: { code: "REST_API_WRITE", transactionOutcome: "rolledBack", cause: { code: "ER_DUP_ENTRY" } }
|
|
256
256
|
});
|
|
257
|
-
const repository = createRepository({ api
|
|
257
|
+
const repository = createRepository({ api });
|
|
258
258
|
|
|
259
259
|
const inserted = await repository.insert({
|
|
260
260
|
slug: "shared-workspace",
|
|
@@ -282,15 +282,15 @@ test("workspacesRepository.updateById patches canonical fields and updatedAt", a
|
|
|
282
282
|
}]
|
|
283
283
|
])
|
|
284
284
|
});
|
|
285
|
-
const repository = createRepository({ api
|
|
285
|
+
const repository = createRepository({ api });
|
|
286
286
|
|
|
287
287
|
await repository.updateById("7", {
|
|
288
288
|
name: "TonyMobily 4"
|
|
289
289
|
});
|
|
290
290
|
|
|
291
291
|
assert.equal(state.patchPayload.id, "7");
|
|
292
|
-
assert.equal(state.patchPayload.
|
|
293
|
-
assert.equal(typeof state.patchPayload.
|
|
292
|
+
assert.equal(state.patchPayload.data.name, "TonyMobily 4");
|
|
293
|
+
assert.equal(typeof state.patchPayload.data.updatedAt, "string");
|
|
294
294
|
});
|
|
295
295
|
|
|
296
296
|
test("workspacesRepository.listForUserId keeps membership fields outside the canonical workspace row", async () => {
|
|
@@ -346,7 +346,7 @@ test("workspacesRepository.listForUserId keeps membership fields outside the can
|
|
|
346
346
|
}
|
|
347
347
|
]
|
|
348
348
|
});
|
|
349
|
-
const repository = createRepository({ api
|
|
349
|
+
const repository = createRepository({ api });
|
|
350
350
|
|
|
351
351
|
const workspaces = await repository.listForUserId("9");
|
|
352
352
|
|