@jskit-ai/users-core 0.1.172 → 0.1.173
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 +41 -253
- package/patterns/user-administration-server/PATTERN.md +64 -0
- package/patterns/user-administration-server/example/packages/users/package.json +42 -0
- package/patterns/user-administration-server/example/packages/users/src/server/UsersFeature.js +13 -0
- package/patterns/user-administration-server/example/packages/users/src/shared/index.js +1 -0
- package/patterns/user-administration-server/example/packages/users-workspace/package.json +43 -0
- package/patterns/user-administration-server/example/packages/users-workspace/src/server/UsersWorkspaceFeature.js +22 -0
- package/patterns/user-administration-server/example/packages/users-workspace/src/shared/userResource.js +56 -0
- package/src/server/UsersExtensionsProvider.js +14 -0
- package/src/server/UsersFeature.js +119 -0
- package/src/server/UsersIdentityProvider.js +57 -0
- package/src/server/accountNotifications/accountNotificationsActions.js +17 -5
- package/src/server/accountNotifications/bootAccountNotificationsRoutes.js +4 -6
- package/src/server/accountPreferences/accountPreferencesActions.js +17 -5
- package/src/server/accountPreferences/bootAccountPreferencesRoutes.js +4 -6
- package/src/server/accountProfile/accountProfileActions.js +28 -14
- package/src/server/accountProfile/bootAccountProfileRoutes.js +11 -10
- package/src/server/accountSecurity/accountSecurityActions.js +27 -17
- package/src/server/accountSecurity/bootAccountSecurityRoutes.js +4 -7
- package/src/server/common/services/authProfileSyncService.js +11 -4
- package/src/server/common/support/realtimeServiceEvents.js +8 -11
- package/src/server/usersBootstrapContributor.js +4 -28
- package/src/server/usersExtensions.js +65 -0
- package/test/authProfileSyncService.test.js +2 -2
- package/test/exportsContract.test.js +6 -1
- package/test/featureRuntime.test.js +78 -0
- package/test/usersBootstrapContributor.test.js +1 -43
- package/test/usersPackageScaffoldContract.test.js +58 -204
- package/test/usersRouteRequestInputValidator.test.js +19 -43
- package/src/server/UsersCoreServiceProvider.js +0 -56
- package/src/server/accountNotifications/registerAccountNotifications.js +0 -37
- package/src/server/accountPreferences/registerAccountPreferences.js +0 -37
- package/src/server/accountProfile/registerAccountProfile.js +0 -56
- package/src/server/accountSecurity/registerAccountSecurity.js +0 -29
- package/src/server/common/registerCommonRepositories.js +0 -23
- package/src/server/common/registerSharedApi.js +0 -9
- package/src/server/profileSyncLifecycleContributorRegistry.js +0 -56
- package/src/server/registerUsersBootstrap.js +0 -20
- package/src/server/registerUsersCore.js +0 -22
- package/templates/packages/users/package.json +0 -82
- package/templates/packages/users/src/server/UsersProvider.js +0 -90
- package/templates/packages/users/src/server/actions.js +0 -68
- package/templates/packages/users/src/server/registerRoutes.js +0 -101
- package/templates/packages/users/src/server/repository.js +0 -51
- package/templates/packages/users/src/server/service.js +0 -32
- package/templates/packages/users-workspace/package.json +0 -83
- package/templates/packages/users-workspace/src/server/UsersProvider.js +0 -91
- package/templates/packages/users-workspace/src/server/actions.js +0 -77
- package/templates/packages/users-workspace/src/server/registerRoutes.js +0 -111
- package/test/providerLifecycle.test.js +0 -63
- package/test/registerCommonRepositories.test.js +0 -57
- package/test/registerServiceRealtimeEvents.test.js +0 -57
- package/test/registerUsersCore.test.js +0 -53
- /package/{templates/migrations → migrations}/users_core_generic_initial.cjs +0 -0
- /package/{templates/migrations → migrations}/users_core_profile_updated_at.cjs +0 -0
- /package/{templates/migrations → migrations}/users_core_profile_username.cjs +0 -0
- /package/{templates → patterns/user-administration-server/example}/packages/users/src/shared/userResource.js +0 -0
- /package/{templates/packages/users → patterns/user-administration-server/example/packages/users-workspace}/src/shared/index.js +0 -0
|
@@ -64,7 +64,11 @@ function normalizeLifecycleContributors(entries = []) {
|
|
|
64
64
|
);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
function createService({
|
|
67
|
+
function createService({
|
|
68
|
+
userProfilesRepository,
|
|
69
|
+
resolveLifecycleContributors = () => [],
|
|
70
|
+
userSettingsRepository = null
|
|
71
|
+
} = {}) {
|
|
68
72
|
if (!userProfilesRepository || typeof userProfilesRepository.findByIdentity !== "function") {
|
|
69
73
|
throw new Error("authProfileSyncService requires userProfilesRepository.findByIdentity().");
|
|
70
74
|
}
|
|
@@ -78,7 +82,9 @@ function createService({ userProfilesRepository, lifecycleContributors = [], use
|
|
|
78
82
|
throw new Error("authProfileSyncService requires userSettingsRepository.ensureForUserId().");
|
|
79
83
|
}
|
|
80
84
|
|
|
81
|
-
|
|
85
|
+
if (typeof resolveLifecycleContributors !== "function") {
|
|
86
|
+
throw new Error("authProfileSyncService requires resolveLifecycleContributors().");
|
|
87
|
+
}
|
|
82
88
|
|
|
83
89
|
async function findByIdentity(identityLike, options = {}) {
|
|
84
90
|
const normalized = buildNormalizedIdentityKey(identityLike);
|
|
@@ -107,6 +113,7 @@ function createService({ userProfilesRepository, lifecycleContributors = [], use
|
|
|
107
113
|
|
|
108
114
|
async function syncIdentityProfile(profileLike, options = {}) {
|
|
109
115
|
const normalized = buildNormalizedIdentityProfile(profileLike);
|
|
116
|
+
const lifecycleContributors = normalizeLifecycleContributors(resolveLifecycleContributors());
|
|
110
117
|
|
|
111
118
|
const runSync = async (trx = null) => {
|
|
112
119
|
const operationOptions = trx ? { ...options, trx } : options;
|
|
@@ -115,7 +122,7 @@ function createService({ userProfilesRepository, lifecycleContributors = [], use
|
|
|
115
122
|
if (!profileNeedsUpdate(existing, normalized)) {
|
|
116
123
|
const synchronizedProfile = requireSynchronizedProfile(existing);
|
|
117
124
|
await userSettingsRepository.ensureForUserId(synchronizedProfile.id, operationOptions);
|
|
118
|
-
for (const contributor of
|
|
125
|
+
for (const contributor of lifecycleContributors) {
|
|
119
126
|
await contributor.afterIdentityProfileSynced({
|
|
120
127
|
profile: synchronizedProfile,
|
|
121
128
|
created,
|
|
@@ -129,7 +136,7 @@ function createService({ userProfilesRepository, lifecycleContributors = [], use
|
|
|
129
136
|
const synchronizedProfile = requireSynchronizedProfile(upserted);
|
|
130
137
|
await userSettingsRepository.ensureForUserId(synchronizedProfile.id, operationOptions);
|
|
131
138
|
created = !existing;
|
|
132
|
-
for (const contributor of
|
|
139
|
+
for (const contributor of lifecycleContributors) {
|
|
133
140
|
await contributor.afterIdentityProfileSynced({
|
|
134
141
|
profile: synchronizedProfile,
|
|
135
142
|
created,
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { deepFreeze } from "./deepFreeze.js";
|
|
1
|
+
import { createEntityChangedActionEvent } from "@jskit-ai/kernel/server/actions";
|
|
3
2
|
|
|
4
|
-
function resolveActorScopedEntityId({
|
|
5
|
-
return
|
|
3
|
+
function resolveActorScopedEntityId({ context } = {}) {
|
|
4
|
+
return context?.actor?.id;
|
|
6
5
|
}
|
|
7
6
|
|
|
8
|
-
const ACCOUNT_SETTINGS_AND_BOOTSTRAP_EVENTS =
|
|
9
|
-
{
|
|
10
|
-
type: "entity.changed",
|
|
7
|
+
const ACCOUNT_SETTINGS_AND_BOOTSTRAP_EVENTS = Object.freeze([
|
|
8
|
+
createEntityChangedActionEvent({
|
|
11
9
|
source: "account",
|
|
12
10
|
entity: "settings",
|
|
13
11
|
operation: "updated",
|
|
@@ -16,9 +14,8 @@ const ACCOUNT_SETTINGS_AND_BOOTSTRAP_EVENTS = deepFreeze([
|
|
|
16
14
|
event: "account.settings.changed",
|
|
17
15
|
audience: "actor_user"
|
|
18
16
|
}
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
type: "entity.changed",
|
|
17
|
+
}),
|
|
18
|
+
createEntityChangedActionEvent({
|
|
22
19
|
source: "users",
|
|
23
20
|
entity: "bootstrap",
|
|
24
21
|
operation: "updated",
|
|
@@ -27,7 +24,7 @@ const ACCOUNT_SETTINGS_AND_BOOTSTRAP_EVENTS = deepFreeze([
|
|
|
27
24
|
event: "users.bootstrap.changed",
|
|
28
25
|
audience: "actor_user"
|
|
29
26
|
}
|
|
30
|
-
}
|
|
27
|
+
})
|
|
31
28
|
]);
|
|
32
29
|
|
|
33
30
|
export { ACCOUNT_SETTINGS_AND_BOOTSTRAP_EVENTS };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AppError } from "@jskit-ai/kernel/server/runtime";
|
|
2
2
|
import { requireServiceMethod } from "@jskit-ai/kernel/shared/actions/actionContributorHelpers";
|
|
3
3
|
import { normalizeLowerText, normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
|
|
4
|
-
import {
|
|
4
|
+
import { normalizeObject } from "@jskit-ai/kernel/shared/support/normalize";
|
|
5
5
|
import { accountAvatarFormatter } from "./common/formatters/accountAvatarFormatter.js";
|
|
6
6
|
import { USER_SETTINGS_BOOTSTRAP_KEYS } from "../shared/resources/userSettingsResource.js";
|
|
7
7
|
|
|
@@ -30,34 +30,13 @@ function getOAuthProviderCatalogPayload(authService) {
|
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
function
|
|
34
|
-
if (value === undefined || value === null || value === "") {
|
|
35
|
-
return fallback;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return normalizeBoolean(value);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function resolveAppState(appConfig = {}) {
|
|
42
|
-
const features = {
|
|
43
|
-
assistantEnabled: resolveBooleanConfigValue(appConfig.assistantEnabled, false),
|
|
44
|
-
assistantRequiredPermission: normalizeText(appConfig.assistantRequiredPermission),
|
|
45
|
-
socialEnabled: resolveBooleanConfigValue(appConfig.socialEnabled, false),
|
|
46
|
-
socialFederationEnabled: resolveBooleanConfigValue(appConfig.socialFederationEnabled, false)
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
features
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function createAnonymousBootstrapPayload({ appState, surfaceAccess = {} }) {
|
|
33
|
+
function createAnonymousBootstrapPayload({ surfaceAccess = {} }) {
|
|
55
34
|
return {
|
|
56
35
|
session: {
|
|
57
36
|
authenticated: false
|
|
58
37
|
},
|
|
59
38
|
profile: null,
|
|
60
|
-
app:
|
|
39
|
+
app: {},
|
|
61
40
|
surfaceAccess: normalizeObject(surfaceAccess),
|
|
62
41
|
userSettings: null,
|
|
63
42
|
requestMeta: {
|
|
@@ -80,11 +59,9 @@ function mapUserSettingsBootstrap(settings = {}) {
|
|
|
80
59
|
function createUsersBootstrapContributor({
|
|
81
60
|
userProfilesRepository,
|
|
82
61
|
userSettingsRepository,
|
|
83
|
-
appConfig = {},
|
|
84
62
|
authService
|
|
85
63
|
} = {}) {
|
|
86
64
|
const contributorId = "users.bootstrap";
|
|
87
|
-
const appState = resolveAppState(appConfig);
|
|
88
65
|
|
|
89
66
|
requireServiceMethod(userProfilesRepository, "findById", contributorId, {
|
|
90
67
|
serviceLabel: "internal.repository.user-profiles"
|
|
@@ -114,7 +91,6 @@ function createUsersBootstrapContributor({
|
|
|
114
91
|
const normalizedUser = authResult?.authenticated === true ? authResult?.actor || null : null;
|
|
115
92
|
const inheritedSurfaceAccess = normalizeObject(existingPayload?.surfaceAccess);
|
|
116
93
|
let payload = createAnonymousBootstrapPayload({
|
|
117
|
-
appState,
|
|
118
94
|
surfaceAccess: inheritedSurfaceAccess
|
|
119
95
|
});
|
|
120
96
|
|
|
@@ -132,7 +108,7 @@ function createUsersBootstrapContributor({
|
|
|
132
108
|
email: latestProfile.email,
|
|
133
109
|
avatar: accountAvatarFormatter(latestProfile, userSettings)
|
|
134
110
|
},
|
|
135
|
-
app:
|
|
111
|
+
app: {},
|
|
136
112
|
surfaceAccess: inheritedSurfaceAccess,
|
|
137
113
|
userSettings: mapUserSettingsBootstrap(userSettings),
|
|
138
114
|
requestMeta: {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
function normalizeContributor(value) {
|
|
2
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
3
|
+
throw new TypeError("Profile synchronization lifecycle contributor must be an object.");
|
|
4
|
+
}
|
|
5
|
+
const contributorId = String(value.contributorId || "").trim();
|
|
6
|
+
if (!contributorId) {
|
|
7
|
+
throw new TypeError("Profile synchronization lifecycle contributor requires contributorId.");
|
|
8
|
+
}
|
|
9
|
+
if (typeof value.afterIdentityProfileSynced !== "function") {
|
|
10
|
+
throw new TypeError(`Profile synchronization lifecycle contributor "${contributorId}" requires afterIdentityProfileSynced().`);
|
|
11
|
+
}
|
|
12
|
+
return Object.freeze({
|
|
13
|
+
...value,
|
|
14
|
+
contributorId,
|
|
15
|
+
order: Number.isFinite(value.order) ? Number(value.order) : 0,
|
|
16
|
+
afterIdentityProfileSynced: value.afterIdentityProfileSynced
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function createUsersExtensions() {
|
|
21
|
+
const contributors = [];
|
|
22
|
+
const contributorIds = new Set();
|
|
23
|
+
let sealed = false;
|
|
24
|
+
|
|
25
|
+
function registerProfileSyncLifecycleContributor(value) {
|
|
26
|
+
if (sealed) {
|
|
27
|
+
throw new Error("User extension registration is closed after profile synchronization starts.");
|
|
28
|
+
}
|
|
29
|
+
const contributor = normalizeContributor(value);
|
|
30
|
+
if (contributorIds.has(contributor.contributorId)) {
|
|
31
|
+
throw new Error(`Profile synchronization lifecycle contributor "${contributor.contributorId}" is duplicated.`);
|
|
32
|
+
}
|
|
33
|
+
contributorIds.add(contributor.contributorId);
|
|
34
|
+
contributors.push(contributor);
|
|
35
|
+
return api;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function profileSyncLifecycleContributors() {
|
|
39
|
+
sealed = true;
|
|
40
|
+
return Object.freeze(
|
|
41
|
+
contributors
|
|
42
|
+
.map((contributor, index) => ({ contributor, index }))
|
|
43
|
+
.sort((left, right) => left.contributor.order - right.contributor.order || left.index - right.index)
|
|
44
|
+
.map(({ contributor }) => contributor)
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function diagnostics() {
|
|
49
|
+
return Object.freeze({
|
|
50
|
+
sealed,
|
|
51
|
+
profileSyncLifecycleContributorIds: Object.freeze(
|
|
52
|
+
contributors.map((entry) => entry.contributorId).sort()
|
|
53
|
+
)
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const api = Object.freeze({
|
|
58
|
+
registerProfileSyncLifecycleContributor,
|
|
59
|
+
profileSyncLifecycleContributors,
|
|
60
|
+
diagnostics
|
|
61
|
+
});
|
|
62
|
+
return api;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { createUsersExtensions };
|
|
@@ -33,7 +33,7 @@ test("authProfileSyncService.syncIdentityProfile uses shared transaction for pro
|
|
|
33
33
|
return { userId: Number(userId) };
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
|
-
|
|
36
|
+
resolveLifecycleContributors: () => [
|
|
37
37
|
{
|
|
38
38
|
contributorId: "test.lifecycle",
|
|
39
39
|
async afterIdentityProfileSynced({ created, options = {} } = {}) {
|
|
@@ -91,7 +91,7 @@ test("authProfileSyncService.syncIdentityProfile skips write path when profile i
|
|
|
91
91
|
return { userId: "7" };
|
|
92
92
|
}
|
|
93
93
|
},
|
|
94
|
-
|
|
94
|
+
resolveLifecycleContributors: () => [
|
|
95
95
|
{
|
|
96
96
|
contributorId: "test.lifecycle",
|
|
97
97
|
async afterIdentityProfileSynced({ created } = {}) {
|
|
@@ -12,7 +12,12 @@ test("users-core exports are explicit and aligned with production/template usage
|
|
|
12
12
|
const result = evaluatePackageExportsContract({
|
|
13
13
|
repoRoot: REPO_ROOT,
|
|
14
14
|
packageDir: PACKAGE_DIR,
|
|
15
|
-
packageId: "@jskit-ai/users-core"
|
|
15
|
+
packageId: "@jskit-ai/users-core",
|
|
16
|
+
requiredExports: [
|
|
17
|
+
"./server/UsersFeature",
|
|
18
|
+
"./server/UsersIdentityProvider",
|
|
19
|
+
"./server/usersExtensions"
|
|
20
|
+
]
|
|
16
21
|
});
|
|
17
22
|
|
|
18
23
|
assert.deepEqual(
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { createActionProvider } from "@jskit-ai/kernel/server/actions";
|
|
4
|
+
import { createCapabilityRuntime, defineProvider } from "@jskit-ai/kernel/shared/capabilities";
|
|
5
|
+
import { createBootstrapRuntime } from "@jskit-ai/kernel/server/runtime";
|
|
6
|
+
import { createAuthExtensions } from "@jskit-ai/auth-core/server/authExtensions";
|
|
7
|
+
import { UsersExtensionsProvider } from "../src/server/UsersExtensionsProvider.js";
|
|
8
|
+
import { UsersFeature } from "../src/server/UsersFeature.js";
|
|
9
|
+
import { UsersIdentityProvider } from "../src/server/UsersIdentityProvider.js";
|
|
10
|
+
|
|
11
|
+
function createJsonRestApiFixture() {
|
|
12
|
+
const resources = {};
|
|
13
|
+
return {
|
|
14
|
+
resources,
|
|
15
|
+
async addResource(scopeName) {
|
|
16
|
+
resources[scopeName] = Object.freeze({
|
|
17
|
+
query: async () => ({ data: [] }),
|
|
18
|
+
post: async () => null,
|
|
19
|
+
patch: async () => null
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
test("UsersFeature assembles account behavior through explicit capabilities", async () => {
|
|
26
|
+
const routes = [];
|
|
27
|
+
const bootstrap = createBootstrapRuntime();
|
|
28
|
+
const authExtensions = createAuthExtensions();
|
|
29
|
+
const jsonRestApi = createJsonRestApiFixture();
|
|
30
|
+
let users = null;
|
|
31
|
+
let actionCatalogue = null;
|
|
32
|
+
const probe = defineProvider({
|
|
33
|
+
id: "test.users.probe",
|
|
34
|
+
requires: {
|
|
35
|
+
actions: "runtime.actions",
|
|
36
|
+
usersCapability: "users.core"
|
|
37
|
+
},
|
|
38
|
+
setup({ actions, usersCapability }) {
|
|
39
|
+
actionCatalogue = actions;
|
|
40
|
+
users = usersCapability;
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
const runtime = createCapabilityRuntime({
|
|
45
|
+
inputs: {
|
|
46
|
+
"auth.extensions": authExtensions,
|
|
47
|
+
"auth.service": {},
|
|
48
|
+
"runtime.bootstrap": bootstrap,
|
|
49
|
+
"runtime.database": { knex() {} },
|
|
50
|
+
"runtime.http": {
|
|
51
|
+
router: {
|
|
52
|
+
register(method, path) {
|
|
53
|
+
routes.push(`${method} ${path}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"runtime.json-rest-api": jsonRestApi,
|
|
58
|
+
"runtime.storage": {
|
|
59
|
+
async getItemRaw() { return null; },
|
|
60
|
+
async setItemRaw() {},
|
|
61
|
+
async removeItem() {}
|
|
62
|
+
},
|
|
63
|
+
"runtime.uploads": { readSingleMultipartFile() {} }
|
|
64
|
+
},
|
|
65
|
+
providers: [createActionProvider(), UsersExtensionsProvider, UsersIdentityProvider, UsersFeature, probe]
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
await runtime.start();
|
|
69
|
+
|
|
70
|
+
assert.deepEqual(Object.keys(jsonRestApi.resources).sort(), ["userProfiles", "userSettings"]);
|
|
71
|
+
assert.equal(typeof users.repositories.userProfiles.findByIdentity, "function");
|
|
72
|
+
assert.equal(typeof users.services.accountProfile.updateProfile, "function");
|
|
73
|
+
assert.equal(routes.includes("GET /api/settings"), true);
|
|
74
|
+
assert.equal(routes.includes("POST /api/settings/security/change-password"), true);
|
|
75
|
+
assert.equal(actionCatalogue.listDefinitions().length, 11);
|
|
76
|
+
assert.deepEqual(bootstrap.diagnostics().contributorIds, ["users.bootstrap"]);
|
|
77
|
+
assert.equal(authExtensions.resolveProfileProjector().projectorId, "users.profile");
|
|
78
|
+
});
|
|
@@ -93,7 +93,7 @@ test("users bootstrap contributor exposes the generic authenticated bootstrap pa
|
|
|
93
93
|
assert.deepEqual(payload.surfaceAccess, {
|
|
94
94
|
consoleowner: true
|
|
95
95
|
});
|
|
96
|
-
assert.
|
|
96
|
+
assert.deepEqual(payload.app, {});
|
|
97
97
|
assert.deepEqual(payload.session.oauthProviders, [
|
|
98
98
|
{
|
|
99
99
|
id: "google",
|
|
@@ -117,9 +117,6 @@ test("users bootstrap contributor emits anonymous bootstrap payload without work
|
|
|
117
117
|
return createUserSettings();
|
|
118
118
|
}
|
|
119
119
|
},
|
|
120
|
-
appConfig: {
|
|
121
|
-
tenancyMode: "none"
|
|
122
|
-
},
|
|
123
120
|
authService: {
|
|
124
121
|
getOAuthProviderCatalog() {
|
|
125
122
|
return {
|
|
@@ -156,42 +153,3 @@ test("users bootstrap contributor emits anonymous bootstrap payload without work
|
|
|
156
153
|
});
|
|
157
154
|
assert.equal(payload.userSettings, null);
|
|
158
155
|
});
|
|
159
|
-
|
|
160
|
-
test("users bootstrap contributor uses shared boolean normalization for app feature flags", async () => {
|
|
161
|
-
const contributor = createUsersBootstrapContributor({
|
|
162
|
-
userProfilesRepository: {
|
|
163
|
-
async findById() {
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
},
|
|
167
|
-
userSettingsRepository: {
|
|
168
|
-
async ensureForUserId() {
|
|
169
|
-
return createUserSettings();
|
|
170
|
-
}
|
|
171
|
-
},
|
|
172
|
-
appConfig: {
|
|
173
|
-
assistantEnabled: "yes",
|
|
174
|
-
socialEnabled: 0,
|
|
175
|
-
socialFederationEnabled: "no"
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
const payload = await contributor.contribute({
|
|
180
|
-
request: {
|
|
181
|
-
async executeAction() {
|
|
182
|
-
return {
|
|
183
|
-
authenticated: false
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
},
|
|
187
|
-
payload: {},
|
|
188
|
-
reply: {}
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
assert.deepEqual(payload.app.features, {
|
|
192
|
-
assistantEnabled: true,
|
|
193
|
-
assistantRequiredPermission: "",
|
|
194
|
-
socialEnabled: false,
|
|
195
|
-
socialFederationEnabled: false
|
|
196
|
-
});
|
|
197
|
-
});
|