Skip to content

Commit 856c89a

Browse files
committed
adding sharing capabilities
1 parent ddd9224 commit 856c89a

2 files changed

Lines changed: 81 additions & 8 deletions

File tree

src/appTemplates/AppManagingAccount.js

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ const collectorIdGenerator = new ShortUniqueId({ dictionary: 'alphanum_lower', l
1212
* Stream structure
1313
* - [baseStreamId] "Root" stream for this app
1414
* - [baseStreamId]-[collectorsId] Each "questionnary" or "request for a set of data" has it's own stream
15-
* - [baseStreamId]-[collectorsId]-settings Contains events with the current settings of this app (this stream will be shared in "read" with the request)
15+
* - [baseStreamId]-[collectorsId]-public Contains events with the current settings of this app (this stream will be shared in "read" with the request)
1616
* - [baseStreamId]-[collectorsId]-pending Contains events with "pending" requests
17+
* - [baseStreamId]-[collectorsId]-inbox Contains events with "inbox" requests Will be shared in createOnly
1718
* - [baseStreamId]-[collectorsId]-active Contains events with "active" users
1819
* - [baseStreamId]-[scollectorsId]-errors Contains events with "revoked" or "erroneous" users
1920
*/
@@ -98,8 +99,9 @@ class AppManagingAccount {
9899
}
99100

100101
const COLLECTOR_STREAMID_SUFFIXES = {
101-
settings: 'settings',
102+
public: 'public',
102103
pending: 'pending',
104+
inbox: 'inbox',
103105
active: 'active',
104106
error: 'error'
105107
};
@@ -110,6 +112,7 @@ class Collector {
110112
streamId;
111113
name;
112114
#streamData;
115+
#cache;
113116

114117
/**
115118
* @param {AppManagingAccount} appManaging
@@ -120,6 +123,63 @@ class Collector {
120123
this.name = streamData.name;
121124
this.appManaging = appManaging;
122125
this.#streamData = streamData;
126+
this.#cache = {};
127+
}
128+
129+
/**
130+
* Get sharing api endpoint
131+
*/
132+
async sharingApiEndpoint () {
133+
if (this.#cache.sharingApiEndpoint) return this.#cache.sharingApiEndpoint;
134+
// check if sharing present
135+
const sharedAccessId = 'a-' + this.streamId;
136+
const accessesCheckRes = (await this.appManaging.connection.api([
137+
{ method: 'accesses.get', params: {} }
138+
]))[0];
139+
if (accessesCheckRes?.error) throw new HDSLibError('Failed getting list of accesses', accessesCheckRes.error);
140+
if (!accessesCheckRes?.accesses) throw new HDSLibError('Failed getting list of accesses', accessesCheckRes);
141+
const sharedAccess = accessesCheckRes.accesses.find(
142+
(access) => access.name === sharedAccessId
143+
);
144+
// found return it
145+
if (sharedAccess) {
146+
this.#cache.sharingApiEndpoint = sharedAccess.apiEndpoint;
147+
return sharedAccess.apiEndpoint;
148+
}
149+
150+
// not found create it
151+
const permissions = [
152+
{ streamId: this.streamIdFor(Collector.STREAMID_SUFFIXES.inbox), level: 'create-only' },
153+
{ streamId: this.streamIdFor(Collector.STREAMID_SUFFIXES.public), level: 'read' },
154+
// for "publicly shared access" always forbid the selfRevoke feature
155+
{ feature: 'selfRevoke', setting: 'forbidden' },
156+
// for "publicly shared access" always forbid the selfAudit feature
157+
{ feature: 'selfAudit', setting: 'forbidden' }
158+
];
159+
const clientData = {
160+
hdsCollector: {
161+
public: {
162+
streamId: this.streamIdFor(Collector.public)
163+
},
164+
inbox: {
165+
streamId: this.streamIdFor(Collector.inbox)
166+
}
167+
}
168+
};
169+
const accessesCreate = await this.appManaging.connection.api([{
170+
method: 'accesses.create',
171+
params: {
172+
name: sharedAccessId,
173+
type: 'shared',
174+
permissions,
175+
clientData
176+
}
177+
}]);
178+
if (accessesCreate?.[0]?.error) throw new HDSLibError('Failed creating shared token', accessesCreate?.[0]?.error);
179+
const newSharingApiEndpoint = accessesCreate?.[0]?.access?.apiEndpoint;
180+
if (!newSharingApiEndpoint) throw new HDSLibError('Cannot find apiEndpoint in sharing creation request', accessesCreate);
181+
this.#cache.sharingApiEndpoint = newSharingApiEndpoint;
182+
return newSharingApiEndpoint;
123183
}
124184

125185
/**
@@ -175,7 +235,7 @@ module.exports = AppManagingAccount;
175235

176236
async function newAppManagingAccountFromConnection (connection, baseStreamId) {
177237
const accessInfo = (await connection.api([{ method: 'getAccessInfo', params: {} }]))[0];
178-
if (!accessInfo.type === 'app') throw new Error('Failed createing new AppManagingAccount, invalid coonection: ' + JSON.stringify(accessInfo));
238+
if (!accessInfo.type === 'app') throw new Error('Failed creating new AppManagingAccount, "app" authorization required: ' + JSON.stringify(accessInfo));
179239
// check if baseStreamId is in pemission set
180240
const found = accessInfo.permissions.find(p => (p.streamId === baseStreamId || p.streamId === '*'));
181241
if (found && found.level !== 'manage') { // check if level 'manage'

tests/apptemplates.test.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ describe('[APTX] appTemplates', function () {
1717
appManaging = await AppManagingAccount.newFromConnection(connection, baseStreamId);
1818
});
1919

20-
it('[APTA] Initial and create One collector', async () => {
20+
it('[APTA] Full flow create collector and sharing', async () => {
2121
assert.equal(appManaging.appName, appName);
2222
assert.equal(appManaging.baseStreamId, baseStreamId);
2323

@@ -48,7 +48,7 @@ describe('[APTX] appTemplates', function () {
4848

4949
// check StreamStructure
5050
const resultCheckStructure = await newCollector.checkStreamStructure();
51-
assert.equal(resultCheckStructure.created.length, 4, 'Should create 4 streams');
51+
assert.equal(resultCheckStructure.created.length, 5, 'Should create 5 streams');
5252
for (const created of resultCheckStructure.created) {
5353
assert.equal(created.parentId, newCollector.streamId, 'Should have collector stream as parentid');
5454
}
@@ -57,15 +57,28 @@ describe('[APTX] appTemplates', function () {
5757
const resultCheckStructure2 = await newCollector.checkStreamStructure();
5858
assert.equal(resultCheckStructure2.created.length, 0, 'Should create 0 streams');
5959

60+
// Sharing token creation
61+
const sharingApiEndpoint = await newCollector.sharingApiEndpoint();
62+
assert.ok(sharingApiEndpoint.startsWith('https://'));
63+
64+
// Should return the same
65+
const sharingApiEndpoint2 = await newCollector.sharingApiEndpoint();
66+
assert.equal(sharingApiEndpoint2, sharingApiEndpoint);
67+
68+
// ---------- creation of a manager on existing structure ---------- //
69+
6070
// creating a new Manager with same connection should load the structure
6171
const connection2 = new pryv.Connection(appManaging.connection.apiEndpoint);
6272
const appManaging2 = await AppManagingAccount.newFromConnection(connection2, baseStreamId);
6373
// check if collector is in the list
6474
const collectors2 = await appManaging2.getCollectors();
65-
const found2 = collectors2.find(c => c.name === collectorName);
66-
if (!found2) throw new Error('Should find collector with name: ' + collectorName);
75+
const collector2 = collectors2.find(c => c.name === collectorName);
76+
if (!collector2) throw new Error('Should find collector with name: ' + collectorName);
6777
// call of StreamStructure should be empty as already created
68-
const resultCheckStructure3 = await newCollector.checkStreamStructure();
78+
const resultCheckStructure3 = await collector2.checkStreamStructure();
6979
assert.equal(resultCheckStructure3.created.length, 0, 'Should create 0 streams');
80+
// should return the same access access point
81+
const sharingApiEndpoint3 = await collector2.sharingApiEndpoint();
82+
assert.equal(sharingApiEndpoint3, sharingApiEndpoint);
7083
});
7184
});

0 commit comments

Comments
 (0)