Skip to content

Commit 14d508c

Browse files
authored
Merge pull request #1 from healthdatasafe/feature/custom-app-settings
Adding custom app settings
2 parents 024df38 + a385960 commit 14d508c

2 files changed

Lines changed: 138 additions & 74 deletions

File tree

src/appTemplates/Application.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,46 @@ class Application {
103103
await createAppStreams(this);
104104
}
105105

106+
/**
107+
* Save anything you want for your app
108+
* @param {Object} content will fully replace any existing content
109+
*/
110+
async setCustomSettings (content) {
111+
const currentCustomSettings = await this.getCustomSettings();
112+
if (currentCustomSettings != null) { // update
113+
const id = this.cache.customSettingsEvent.id;
114+
const updatedEvent = await this.connection.apiOne('events.update', { id, update: { content } }, 'event');
115+
this.cache.customSettingsEvent = updatedEvent;
116+
} else {
117+
await this.#createCustomSettings(content);
118+
}
119+
return this.cache.customSettingsEvent?.content;
120+
}
121+
122+
/**
123+
* @private
124+
* Used by getCustomSettings & setCustomSettings
125+
* @param {*} content
126+
*/
127+
async #createCustomSettings (content) {
128+
const createdEvent = await this.connection.apiOne('events.create', { streamIds: [this.baseStreamId], type: 'settings/any', content }, 'event');
129+
this.cache.customSettingsEvent = createdEvent;
130+
}
131+
132+
/**
133+
* Get current settings previously set with setCustomSettings()
134+
*/
135+
async getCustomSettings (forceRefresh = false) {
136+
if (forceRefresh || this.cache.customSettingsEvent) {
137+
const customSettingsEvent = (await this.connection.apiOne('events.get', { streams: [this.baseStreamId], types: ['settings/any'], limit: 1 }, 'events'))[0];
138+
this.cache.customSettingsEvent = customSettingsEvent;
139+
}
140+
if (!this.cache.customSettingsEvent) {
141+
await this.#createCustomSettings({});
142+
}
143+
return this.cache.customSettingsEvent?.content;
144+
}
145+
106146
/**
107147
* Force loading of streamData
108148
*/

tests/applicationClass.test.js

Lines changed: 98 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -13,101 +13,125 @@ describe('[APAX] Application class', () => {
1313
user = await createUserAndPermissions(null, permissionsManager, initialStreams, appName);
1414
});
1515

16-
it('[APAI] Application extension', async () => {
17-
class Dummy extends Application { }
16+
describe('[APIX] Application class internal', () => {
17+
it('[APIS] Application custom settings', async () => {
18+
class Dummy extends Application {
19+
get appSettings () {
20+
return { };
21+
}
22+
}
23+
const appDummy = await Dummy.newFromApiEndpoint(baseStreamId, user.appApiEndpoint, appName);
24+
25+
const settings = await appDummy.getCustomSettings();
26+
assert.deepEqual(settings, {});
27+
const newSettings = { hello: 'Tom', value: 2 };
28+
const newSettings1 = await appDummy.setCustomSettings(newSettings);
29+
assert.deepEqual(newSettings, newSettings1);
30+
const newSettings2 = await appDummy.getCustomSettings();
31+
assert.deepEqual(newSettings, newSettings2);
32+
// assert.equal(newSettings1, newSettings2, 'should be the same object');
33+
const newSettings3 = await appDummy.getCustomSettings(true);
34+
assert.deepEqual(newSettings1, newSettings3);
35+
});
36+
});
37+
38+
describe('[APAE] Application class errors', () => {
39+
it('[APAI] Application extension', async () => {
40+
class Dummy extends Application { }
1841

19-
try {
20-
await Dummy.newFromApiEndpoint(baseStreamId, user.appApiEndpoint, appName);
21-
throw new Error('Should throw an error');
22-
} catch (e) {
23-
assert.equal(e.message, 'appSettings must be implemented');
24-
}
42+
try {
43+
await Dummy.newFromApiEndpoint(baseStreamId, user.appApiEndpoint, appName);
44+
throw new Error('Should throw an error');
45+
} catch (e) {
46+
assert.equal(e.message, 'appSettings must be implemented');
47+
}
2548

26-
try {
49+
try {
2750
// eslint-disable-next-line no-new
28-
new Dummy('u', user.appApiEndpoint, appName);
29-
throw new Error('Should throw an error');
30-
} catch (e) {
31-
assert.equal(e.message, 'Missing or too short baseStreamId');
32-
}
33-
});
51+
new Dummy('u', user.appApiEndpoint, appName);
52+
throw new Error('Should throw an error');
53+
} catch (e) {
54+
assert.equal(e.message, 'Missing or too short baseStreamId');
55+
}
56+
});
3457

35-
it('[APAA] Application name form accessInfo fails in not in settings', () => {
36-
class Dummy extends Application {
37-
get appSettings () {
38-
return { };
58+
it('[APAA] Application name form accessInfo fails in not in settings', () => {
59+
class Dummy extends Application {
60+
get appSettings () {
61+
return { };
62+
}
3963
}
40-
}
4164

42-
try {
65+
try {
4366
// eslint-disable-next-line no-new
44-
new Dummy(baseStreamId, user.appApiEndpoint);
45-
throw new Error('Should throw an error');
46-
} catch (e) {
47-
assert.equal(e.message, 'appName must be given unless appSettings.appNameFromAccessInfo = true');
48-
}
49-
});
67+
new Dummy(baseStreamId, user.appApiEndpoint);
68+
throw new Error('Should throw an error');
69+
} catch (e) {
70+
assert.equal(e.message, 'appName must be given unless appSettings.appNameFromAccessInfo = true');
71+
}
72+
});
5073

51-
it('[APAB] Application name form accessInfo', async () => {
52-
class Dummy2 extends Application {
53-
get appSettings () {
54-
return {
55-
appNameFromAccessInfo: true
56-
};
74+
it('[APAB] Application name form accessInfo', async () => {
75+
class Dummy2 extends Application {
76+
get appSettings () {
77+
return {
78+
appNameFromAccessInfo: true
79+
};
80+
}
5781
}
58-
}
5982

60-
const dummy2 = await Dummy2.newFromApiEndpoint(baseStreamId, user.appApiEndpoint);
61-
assert.ok(dummy2, 'if appSettings.appNameFromAccessInfo = true appName is not required');
62-
});
83+
const dummy2 = await Dummy2.newFromApiEndpoint(baseStreamId, user.appApiEndpoint);
84+
assert.ok(dummy2, 'if appSettings.appNameFromAccessInfo = true appName is not required');
85+
});
6386

64-
it('[APAC] Application should throw error if baseStream is not accessible', async () => {
65-
class Dummy extends Application {
66-
get appSettings () {
67-
return { };
87+
it('[APAC] Application should throw error if baseStream is not accessible', async () => {
88+
class Dummy extends Application {
89+
get appSettings () {
90+
return { };
91+
}
6892
}
69-
}
7093

71-
try {
94+
try {
7295
// eslint-disable-next-line no-new
73-
await Dummy.newFromApiEndpoint('uuuu', user.appApiEndpoint, appName);
74-
throw new Error('Should throw an error');
75-
} catch (e) {
76-
assert.equal(e.message, 'Application with "app" type of access requires (streamId = "uuuu", level = "manage") or master access');
77-
}
78-
});
96+
await Dummy.newFromApiEndpoint('uuuu', user.appApiEndpoint, appName);
97+
throw new Error('Should throw an error');
98+
} catch (e) {
99+
assert.equal(e.message, 'Application with "app" type of access requires (streamId = "uuuu", level = "manage") or master access');
100+
}
101+
});
79102

80-
it('[APAD] Application should throw error if personaToken are not explicity allowed', async () => {
81-
class Dummy extends Application {
82-
get appSettings () {
83-
return { };
103+
it('[APAD] Application should throw error if personaToken are not explicity allowed', async () => {
104+
class Dummy extends Application {
105+
get appSettings () {
106+
return { };
107+
}
84108
}
85-
}
86109

87-
try {
110+
try {
88111
// eslint-disable-next-line no-new
89-
await Dummy.newFromApiEndpoint('uuuu', user.personalApiEndpoint, appName);
90-
throw new Error('Should throw an error');
91-
} catch (e) {
92-
assert.equal(e.message, 'Application should not use a personal token');
93-
}
94-
});
112+
await Dummy.newFromApiEndpoint('uuuu', user.personalApiEndpoint, appName);
113+
throw new Error('Should throw an error');
114+
} catch (e) {
115+
assert.equal(e.message, 'Application should not use a personal token');
116+
}
117+
});
95118

96-
it('[APAE] Application should throw error if master token not provided and required', async () => {
97-
class Dummy extends Application {
98-
get appSettings () {
99-
return {
100-
mustBemaster: true
101-
};
119+
it('[APAE] Application should throw error if master token not provided and required', async () => {
120+
class Dummy extends Application {
121+
get appSettings () {
122+
return {
123+
mustBemaster: true
124+
};
125+
}
102126
}
103-
}
104127

105-
try {
128+
try {
106129
// eslint-disable-next-line no-new
107-
await Dummy.newFromApiEndpoint('uuuu', user.appApiEndpoint, appName);
108-
throw new Error('Should throw an error');
109-
} catch (e) {
110-
assert.equal(e.message, 'Application with "app" type of access requires "master" token (streamId = "*", level = "manage")');
111-
}
130+
await Dummy.newFromApiEndpoint('uuuu', user.appApiEndpoint, appName);
131+
throw new Error('Should throw an error');
132+
} catch (e) {
133+
assert.equal(e.message, 'Application with "app" type of access requires "master" token (streamId = "*", level = "manage")');
134+
}
135+
});
112136
});
113137
});

0 commit comments

Comments
 (0)