Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Lightweight MATLAB helpers and API clients for working with EBRAINS services: au
- List, upload, download, rename, and delete objects in Data Proxy buckets, and compute bucket sizes
- Sync a local folder to a bucket, or a bucket to a local folder, sending only new and changed files
- Read, create, update, release, and delete Knowledge Graph instances, and run dynamic queries
- Search and create collabs through the Collaboratory API
- Search and create collabs, and add users to their teams, through the Collaboratory API

## Requirements
- MATLAB R2022b or newer. The virtual bucket browser (`ebrains.bucket.app.VirtualBucketBrowser`) needs R2023a or newer; the functions work on R2022b.
Expand Down Expand Up @@ -98,11 +98,12 @@ Lookups default to the `RELEASED` stage. Pass a stage vector in order of prefere
instance = kgClient.getInstance(identifier, ["RELEASED", "IN_PROGRESS"]);
```

### 4) Search and create collabs
### 4) Search and create collabs, add team members
```matlab
collabClient = ebrains.collab.api.CollabsClient();
collabs = collabClient.searchCollabs(limit=50);
collabClient.createCollab("my-collab", "My collab", public=false)
collabClient.addTeamUser("my-collab", "editor", "username")
```

The requests go to the Collaboratory server of the `Environment` preference. `ebrains.useEnvironment("int")` switches the current MATLAB session to the integration environment: logins then go to `iam-int.ebrains.eu`, collab requests to `wiki-int.ebrains.eu` and bucket requests to `data-proxy-int.ebrains.eu`. `ebrains.useEnvironment("prod")` switches back. The toolbox keeps one login per environment. The Knowledge Graph clients take their server from the `Server` option of each method instead.
Expand Down
37 changes: 37 additions & 0 deletions src/ebrains/+ebrains/+collab/+api/CollabsClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
% client = ebrains.collab.api.CollabsClient();
% collabs = client.searchCollabs(search="dataset", limit=50);
% client.createCollab("my-collab", "My collab", public=false)
% client.addTeamUser("my-collab", "editor", "username")
%
% See also ebrains.useEnvironment,
% ebrains.common.constant.CollabApiBaseUrl
Expand Down Expand Up @@ -118,5 +119,41 @@ function createCollab(obj, name, title, collabOptions)
obj.throwError("createCollab", response)
end
end

function addTeamUser(obj, collabId, role, username)
% addTeamUser - Add a user to the team of a collab
%
% client.addTeamUser(collabId, role, username) gives the user a
% role in the collab. collabId is the name of the collab, as in
% the name field of the collabs that searchCollabs returns. role
% is "viewer", "editor" or "administrator".
%
% The API documents these failures: 400 (Bad Request) when the
% user does not exist, 403 (Forbidden) when the account sending
% the request is not an administrator of the collab, and 404
% (Not Found) when the collab does not exist.

arguments
obj (1,1) ebrains.collab.api.CollabsClient
collabId (1,1) string {mustBeNonzeroLengthText}
role (1,1) string {mustBeMember(role, ["viewer", "editor", "administrator"])}
username (1,1) string {mustBeNonzeroLengthText}
end

% The endpoint is a PUT without a body, which MATLAB warns
% about on purpose. The warning is off for this request only.
warnState = warning('off', 'MATLAB:http:BodyExpectedFor');
warningCleanup = onCleanup(@() warning(warnState));

request = obj.initializeRequestMessage("PUT");
apiUri = ebrains.common.internal.buildApiUri(...
ebrains.common.constant.CollabApiBaseUrl(), ...
["collabs", collabId, "team", role, "users", username]);
response = obj.sendRequest(request, apiUri);

if response.StatusCode ~= matlab.net.http.StatusCode.NoContent
obj.throwError("addTeamUser", response)
end
end
end
end
55 changes: 55 additions & 0 deletions tests/+ebrains/+test/+collab/+api/CollabsClientTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -129,5 +129,60 @@ function testCreateCollabUnauthorized(testCase)
testCase.verifyError(@() testCase.Client.createCollab("my-collab", "My collab"), ...
'EBRAINS:Collab:createCollab:Unauthorized');
end

function testAddTeamUserSendsPutWithoutBody(testCase)
testCase.Client.addResponse('NoContent', '');

testCase.Client.addTeamUser("my-collab", "editor", "jdoe");

testCase.Client.verifyRequestMethod(1, 'PUT');
testCase.Client.verifyRequestURL(1, ...
'https://wiki.ebrains.eu/rest/v1/collabs/my-collab/team/editor/users/jdoe');
request = testCase.Client.getRequest(1);
testCase.verifyEmpty(request.RequestMessage.Body);
end

function testAddTeamUserRestoresTheBodyWarning(testCase)
% The body-less PUT turns the warning off for the request only,
% also when the request ends in an error.
warningId = 'MATLAB:http:BodyExpectedFor';
originalState = warning('query', warningId);
testCase.addTeardown(@() warning(originalState));
warning('on', warningId);
testCase.Client.addResponse('NoContent', '');
testCase.Client.addResponse('NotFound', 'Collab not found.');

testCase.Client.addTeamUser("my-collab", "editor", "jdoe");
testCase.verifyEqual(warning('query', warningId).state, 'on');

testCase.verifyError(@() testCase.Client.addTeamUser("my-collab", "editor", "jdoe"), ...
'EBRAINS:Collab:addTeamUser:NotFound');
testCase.verifyEqual(warning('query', warningId).state, 'on');
end

function testAddTeamUserRejectsUnknownRole(testCase)
testCase.verifyError(@() testCase.Client.addTeamUser("my-collab", "owner", "jdoe"), ...
'MATLAB:validators:mustBeMember');
testCase.verifyEqual(testCase.Client.getRequestCount(), 0);
end

function testAddTeamUserUnknownUser(testCase)
testCase.Client.addResponse('BadRequest', 'The specified user does not exist.');
testCase.verifyError(@() testCase.Client.addTeamUser("my-collab", "editor", "nobody"), ...
'EBRAINS:Collab:addTeamUser:BadRequest');
end

function testAddTeamUserForbidden(testCase)
testCase.Client.addResponse('Forbidden', ...
'You must be an administrator of this collab to perform this action.');
testCase.verifyError(@() testCase.Client.addTeamUser("my-collab", "editor", "jdoe"), ...
'EBRAINS:Collab:addTeamUser:Forbidden');
end

function testAddTeamUserCollabNotFound(testCase)
testCase.Client.addResponse('NotFound', 'Collab not found.');
testCase.verifyError(@() testCase.Client.addTeamUser("no-such-collab", "editor", "jdoe"), ...
'EBRAINS:Collab:addTeamUser:NotFound');
end
end
end
Loading