From 7f9f92f17708f369dc3ffbe5058f5ac7c15d0879 Mon Sep 17 00:00:00 2001 From: Odei Maiz <33152403+odeimaiz@users.noreply.github.com> Date: Wed, 11 Dec 2024 12:28:36 +0100 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=8E=A8=20[Frontend]=20Enh:=20users=20?= =?UTF-8?q?are=20identified=20by=20username=20(#6934)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/source/class/osparc/auth/Data.js | 5 +-- .../source/class/osparc/auth/Manager.js | 4 +- .../source/class/osparc/dashboard/CardBase.js | 2 +- .../source/class/osparc/data/model/Group.js | 4 ++ .../source/class/osparc/data/model/User.js | 36 ++++++++++------ .../class/osparc/desktop/MainPageHandler.js | 2 +- .../desktop/organizations/MembersList.js | 42 +++++++++---------- .../osparc/desktop/wallets/MembersList.js | 12 +++--- .../osparc/filter/CollaboratorToggleButton.js | 9 ++-- .../class/osparc/share/Collaborators.js | 18 +++----- .../source/class/osparc/store/Groups.js | 22 +++++++--- .../client/source/class/osparc/store/Store.js | 12 ++++-- .../client/source/class/osparc/utils/Utils.js | 5 +++ 13 files changed, 101 insertions(+), 72 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/auth/Data.js b/services/static-webserver/client/source/class/osparc/auth/Data.js index 4a55c04633d..306d3032558 100644 --- a/services/static-webserver/client/source/class/osparc/auth/Data.js +++ b/services/static-webserver/client/source/class/osparc/auth/Data.js @@ -82,12 +82,9 @@ qx.Class.define("osparc.auth.Data", { event: "changeUsername", }, - /** - * Email of logged in user, otherwise null - */ email: { init: null, - nullable: true, + nullable: true, // email of logged in user, otherwise null check: "String" }, diff --git a/services/static-webserver/client/source/class/osparc/auth/Manager.js b/services/static-webserver/client/source/class/osparc/auth/Manager.js index 0cdda5c49b6..ca497e5eabb 100644 --- a/services/static-webserver/client/source/class/osparc/auth/Manager.js +++ b/services/static-webserver/client/source/class/osparc/auth/Manager.js @@ -241,8 +241,8 @@ qx.Class.define("osparc.auth.Manager", { authData.set({ email: profile["login"], username: profile["userName"], - firstName: profile["first_name"] || profile["login"], - lastName: profile["last_name"] || "", + firstName: profile["first_name"], + lastName: profile["last_name"], expirationDate: "expirationDate" in profile ? new Date(profile["expirationDate"]) : null }); }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js index 3bcd200c2ee..0d058644bce 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js @@ -674,7 +674,7 @@ qx.Class.define("osparc.dashboard.CardBase", { __showBlockedCardFromStatus: function(lockedStatus) { const status = lockedStatus["status"]; const owner = lockedStatus["owner"]; - let toolTip = osparc.utils.Utils.firstsUp(owner["first_name"] || this.tr("A user"), owner["last_name"] || ""); + let toolTip = osparc.utils.Utils.firstsUp(owner["first_name"] || this.tr("A user"), owner["last_name"] || ""); // it will be replaced by "userName" let image = null; switch (status) { case "CLOSING": diff --git a/services/static-webserver/client/source/class/osparc/data/model/Group.js b/services/static-webserver/client/source/class/osparc/data/model/Group.js index 3afec8ca34b..e345265ba68 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Group.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Group.js @@ -104,6 +104,10 @@ qx.Class.define("osparc.data.model.Group", { return Object.values(this.getGroupMembers()).find(user => user.getUserId() === userId); }, + getGroupMemberByUsername: function(username) { + return Object.values(this.getGroupMembers()).find(user => user.getUsername() === username); + }, + getGroupMemberByLogin: function(userEmail) { return Object.values(this.getGroupMembers()).find(user => user.getEmail() === userEmail); }, diff --git a/services/static-webserver/client/source/class/osparc/data/model/User.js b/services/static-webserver/client/source/class/osparc/data/model/User.js index dc943c3202a..f0c8a5dabb1 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/User.js +++ b/services/static-webserver/client/source/class/osparc/data/model/User.js @@ -28,22 +28,27 @@ qx.Class.define("osparc.data.model.User", { construct: function(userData) { this.base(arguments); - let label = userData["login"]; + let description = ""; if (userData["first_name"]) { - label = qx.lang.String.firstUp(userData["first_name"]); + description = userData["first_name"]; if (userData["last_name"]) { - label += " " + qx.lang.String.firstUp(userData["last_name"]); + description += " " + userData["last_name"]; } + description += " - "; + } + if (userData["login"]) { + description += userData["login"]; } const thumbnail = osparc.utils.Avatar.emailToThumbnail(userData["login"]); this.set({ - userId: userData["id"], - groupId: userData["gid"], - label: label, - username: userData["username"] || "", + userId: parseInt(userData["id"]), + groupId: parseInt(userData["gid"]), + username: userData["userName"], firstName: userData["first_name"], lastName: userData["last_name"], email: userData["login"], + label: userData["userName"], + description, thumbnail, }); }, @@ -70,24 +75,31 @@ qx.Class.define("osparc.data.model.User", { event: "changeLabel", }, - username: { + description: { check: "String", nullable: true, init: null, + event: "changeDescription", + }, + + username: { + check: "String", + nullable: false, + init: null, event: "changeUsername", }, firstName: { - init: "", - nullable: true, check: "String", + nullable: true, + init: "", event: "changeFirstName" }, lastName: { - init: "", - nullable: true, check: "String", + nullable: true, + init: "", event: "changeLastName" }, diff --git a/services/static-webserver/client/source/class/osparc/desktop/MainPageHandler.js b/services/static-webserver/client/source/class/osparc/desktop/MainPageHandler.js index 0872a1b627b..6e34be5d88c 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/MainPageHandler.js +++ b/services/static-webserver/client/source/class/osparc/desktop/MainPageHandler.js @@ -94,7 +94,7 @@ qx.Class.define("osparc.desktop.MainPageHandler", { lockedBy = studyData["state"]["locked"]["owner"]; } if (locked && lockedBy["user_id"] !== osparc.auth.Data.getInstance().getUserId()) { - const msg = `${studyAlias} ${qx.locale.Manager.tr("is already open by")} ${ + const msg = `${studyAlias} ${qx.locale.Manager.tr("is already open by")} ${ // it will be replaced "userName" "first_name" in lockedBy && lockedBy["first_name"] != null ? lockedBy["first_name"] : qx.locale.Manager.tr("another user.") diff --git a/services/static-webserver/client/source/class/osparc/desktop/organizations/MembersList.js b/services/static-webserver/client/source/class/osparc/desktop/organizations/MembersList.js index 0a3df0faa3b..acd68c25680 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/organizations/MembersList.js +++ b/services/static-webserver/client/source/class/osparc/desktop/organizations/MembersList.js @@ -70,8 +70,8 @@ qx.Class.define("osparc.desktop.organizations.MembersList", { if (sorted !== 0) { return sorted; } - if (("email" in a) && ("email" in b)) { - return a["email"].localeCompare(b["email"]); + if (("label" in a) && ("label" in b)) { + return a["label"].localeCompare(b["label"]); } return 0; } @@ -105,22 +105,17 @@ qx.Class.define("osparc.desktop.organizations.MembersList", { alignY: "middle" })); - const userEmail = new qx.ui.form.TextField().set({ + const newMemberUserName = new qx.ui.form.TextField().set({ required: true, - placeholder: this.tr(" New Member's email") + placeholder: this.tr(" New Member's username") }); - hBox.add(userEmail, { + hBox.add(newMemberUserName, { flex: 1 }); - const validator = new qx.ui.form.validation.Manager(); - validator.add(userEmail, qx.util.Validate.email()); - const addBtn = new qx.ui.form.Button(this.tr("Add")); addBtn.addListener("execute", function() { - if (validator.validate()) { - this.__addMember(userEmail.getValue()); - } + this.__addMember(newMemberUserName.getValue()); }, this); hBox.add(addBtn); @@ -154,9 +149,9 @@ qx.Class.define("osparc.desktop.organizations.MembersList", { ctrl.bindProperty("userId", "model", null, item, id); ctrl.bindProperty("userId", "key", null, item, id); ctrl.bindProperty("thumbnail", "thumbnail", null, item, id); - ctrl.bindProperty("name", "title", null, item, id); + ctrl.bindProperty("label", "title", null, item, id); + ctrl.bindProperty("description", "subtitleMD", null, item, id); ctrl.bindProperty("accessRights", "accessRights", null, item, id); - ctrl.bindProperty("email", "subtitleMD", null, item, id); ctrl.bindProperty("options", "options", null, item, id); ctrl.bindProperty("showOptions", "showOptions", null, item, id); }, @@ -217,7 +212,7 @@ qx.Class.define("osparc.desktop.organizations.MembersList", { const canIDelete = organization.getAccessRights()["delete"]; const introText = canIWrite ? - this.tr("You can add new members and promote or demote existing ones.") : + this.tr("You can add new members and promote or demote existing ones.
In order to add new members, type their username or email if this is public.") : this.tr("You can't add new members to this Organization. Please contact an Administrator or Manager."); this.__introLabel.setValue(introText); @@ -225,15 +220,17 @@ qx.Class.define("osparc.desktop.organizations.MembersList", { enabled: canIWrite }); + const myGroupId = osparc.auth.Data.getInstance().getGroupId(); const membersList = []; const groupMembers = organization.getGroupMembers(); Object.values(groupMembers).forEach(groupMember => { + const gid = parseInt(groupMember.getGroupId()); const member = {}; - member["userId"] = groupMember.getUserId(); - member["groupId"] = groupMember.getGroupId(); + member["userId"] = gid === myGroupId ? osparc.auth.Data.getInstance().getUserId() : groupMember.getUserId(); + member["groupId"] = gid; member["thumbnail"] = groupMember.getThumbnail(); - member["name"] = groupMember.getLabel(); - member["email"] = groupMember.getEmail(); + member["label"] = groupMember.getLabel(); + member["description"] = gid === myGroupId ? osparc.auth.Data.getInstance().getEmail() : groupMember.getDescription(); member["accessRights"] = groupMember.getAccessRights(); let options = []; if (canIDelete) { @@ -287,7 +284,6 @@ qx.Class.define("osparc.desktop.organizations.MembersList", { } // Let me go? const openStudy = osparc.store.Store.getInstance().getCurrentStudy(); - const myGroupId = osparc.store.Groups.getInstance().getMyGroupId(); if ( openStudy === null && canIWrite && @@ -303,16 +299,18 @@ qx.Class.define("osparc.desktop.organizations.MembersList", { membersList.forEach(member => membersModel.append(qx.data.marshal.Json.createModel(member))); }, - __addMember: async function(orgMemberEmail) { + __addMember: async function(newMemberIdentifier) { if (this.__currentOrg === null) { return; } const orgId = this.__currentOrg.getGroupId(); const groupsStore = osparc.store.Groups.getInstance(); - groupsStore.postMember(orgId, orgMemberEmail) + const isEmail = osparc.utils.Utils.isEmail(newMemberIdentifier); + const request = isEmail ? groupsStore.addMember(orgId, null, newMemberIdentifier) : groupsStore.addMember(orgId, newMemberIdentifier); + request .then(newMember => { - const text = orgMemberEmail + this.tr(" successfully added"); + const text = newMemberIdentifier + this.tr(" successfully added"); osparc.FlashMessenger.getInstance().logAs(text); this.__reloadOrgMembers(); diff --git a/services/static-webserver/client/source/class/osparc/desktop/wallets/MembersList.js b/services/static-webserver/client/source/class/osparc/desktop/wallets/MembersList.js index 4f5b1dd796a..dc27a0cfee3 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/wallets/MembersList.js +++ b/services/static-webserver/client/source/class/osparc/desktop/wallets/MembersList.js @@ -70,8 +70,8 @@ qx.Class.define("osparc.desktop.wallets.MembersList", { if (sorted !== 0) { return sorted; } - if (("email" in a) && ("email" in b)) { - return a["email"].localeCompare(b["email"]); + if (("label" in a) && ("label" in b)) { + return a["label"].localeCompare(b["label"]); } return 0; } @@ -170,9 +170,9 @@ qx.Class.define("osparc.desktop.wallets.MembersList", { ctrl.bindProperty("userId", "key", null, item, id); ctrl.bindProperty("groupId", "gid", null, item, id); ctrl.bindProperty("thumbnail", "thumbnail", null, item, id); - ctrl.bindProperty("name", "title", null, item, id); + ctrl.bindProperty("label", "title", null, item, id); + ctrl.bindProperty("description", "subtitleMD", null, item, id); ctrl.bindProperty("accessRights", "accessRights", null, item, id); - ctrl.bindProperty("email", "subtitleMD", null, item, id); ctrl.bindProperty("options", "options", null, item, id); ctrl.bindProperty("showOptions", "showOptions", null, item, id); }, @@ -222,8 +222,8 @@ qx.Class.define("osparc.desktop.wallets.MembersList", { collaborator["userId"] = gid === myGroupId ? osparc.auth.Data.getInstance().getUserId() : collab.getUserId(); collaborator["groupId"] = collab.getGroupId(); collaborator["thumbnail"] = collab.getThumbnail(); - collaborator["name"] = collab.getLabel(); - collaborator["email"] = gid === myGroupId ? osparc.auth.Data.getInstance().getEmail() : collab.getEmail(); + collaborator["label"] = collab.getLabel(); + collaborator["description"] = gid === myGroupId ? osparc.auth.Data.getInstance().getEmail() : collab.getDescription(); collaborator["accessRights"] = { read: accessRights["read"], write: accessRights["write"], diff --git a/services/static-webserver/client/source/class/osparc/filter/CollaboratorToggleButton.js b/services/static-webserver/client/source/class/osparc/filter/CollaboratorToggleButton.js index 23c061cebd3..0008c5d7c48 100644 --- a/services/static-webserver/client/source/class/osparc/filter/CollaboratorToggleButton.js +++ b/services/static-webserver/client/source/class/osparc/filter/CollaboratorToggleButton.js @@ -24,13 +24,16 @@ qx.Class.define("osparc.filter.CollaboratorToggleButton", { }); let label = collaborator.getLabel(); - if ("getEmail" in collaborator) { - // user + if ("getEmail" in collaborator && collaborator.getEmail()) { label += ` (${collaborator.getEmail()})`; - this.setToolTipText(collaborator.getEmail()); } this.setLabel(label); + if (collaborator.getDescription()) { + const ttt = collaborator.getLabel() + "
" + collaborator.getDescription(); + this.setToolTipText(ttt); + } + let iconPath = null; switch (collaborator["collabType"]) { case 0: diff --git a/services/static-webserver/client/source/class/osparc/share/Collaborators.js b/services/static-webserver/client/source/class/osparc/share/Collaborators.js index 426d201d2ab..b012119b025 100644 --- a/services/static-webserver/client/source/class/osparc/share/Collaborators.js +++ b/services/static-webserver/client/source/class/osparc/share/Collaborators.js @@ -293,10 +293,8 @@ qx.Class.define("osparc.share.Collaborators", { ctrl.bindProperty("gid", "key", null, item, id); ctrl.bindProperty("collabType", "collabType", null, item, id); ctrl.bindProperty("thumbnail", "thumbnail", null, item, id); - ctrl.bindProperty("name", "title", null, item, id); // user - ctrl.bindProperty("label", "title", null, item, id); // organization - ctrl.bindProperty("email", "subtitleMD", null, item, id); // user - ctrl.bindProperty("description", "subtitle", null, item, id); // organization + ctrl.bindProperty("label", "title", null, item, id); + ctrl.bindProperty("description", "subtitleMD", null, item, id); ctrl.bindProperty("resourceType", "resourceType", null, item, id); // Resource type ctrl.bindProperty("accessRights", "accessRights", null, item, id); ctrl.bindProperty("showOptions", "showOptions", null, item, id); @@ -409,15 +407,11 @@ qx.Class.define("osparc.share.Collaborators", { const collaborator = { "gid": collab.getGroupId(), "thumbnail": collab.getThumbnail(), + "label": collab.getLabel(), + "description": collab.getDescription(), }; - if ("getUserId" in collab) { - // user - collaborator["name"] = collab.getLabel(); - collaborator["email"] = collab.getEmail(); - } else { - // org/group - collaborator["label"] = collab.getLabel(); - collaborator["description"] = collab.getDescription(); + if (!("getUserId" in collab)) { + // orgnanization if (everyoneGIds.includes(parseInt(gid))) { collaborator["thumbnail"] = "@FontAwesome5Solid/globe/32"; } else if (!collaborator["thumbnail"]) { diff --git a/services/static-webserver/client/source/class/osparc/store/Groups.js b/services/static-webserver/client/source/class/osparc/store/Groups.js index 862f97d06b1..21e2d4b2a29 100644 --- a/services/static-webserver/client/source/class/osparc/store/Groups.js +++ b/services/static-webserver/client/source/class/osparc/store/Groups.js @@ -231,6 +231,7 @@ qx.Class.define("osparc.store.Groups", { }, getOrganization: function(groupId) { + groupId = parseInt(groupId); if (groupId && groupId in this.getOrganizations()) { return this.getOrganizations()[groupId]; } @@ -261,6 +262,14 @@ qx.Class.define("osparc.store.Groups", { return null; }, + getGroupMemberByUsername: function(orgId, username) { + const org = this.getGroup(orgId); + if (org) { + return org.getGroupMemberByUsername(username); + } + return null; + }, + getGroupMemberByLogin: function(orgId, userEmail) { const org = this.getGroup(orgId); if (org) { @@ -330,16 +339,19 @@ qx.Class.define("osparc.store.Groups", { // CRUD GROUP // CRUD GROUP MEMBERS - postMember: function(orgId, newMemberEmail) { + addMember: function(orgId, username, email = null) { const gid = parseInt(orgId); const params = { url: { "gid": gid }, - data: { - "email": newMemberEmail - } + data: {}, }; + if (email) { + params.data["email"] = email; + } else { + params.data["userName"] = username; + } return osparc.data.Resources.fetch("organizationMembers", "post", params) .then(() => { // the backend doesn't return the user back, @@ -347,7 +359,7 @@ qx.Class.define("osparc.store.Groups", { return this.__fetchGroupMembers(gid); }) .then(() => { - const groupMember = this.getGroupMemberByLogin(gid, newMemberEmail); + const groupMember = email ? this.getGroupMemberByLogin(gid, email) : this.getGroupMemberByUsername(gid, username); if (groupMember) { return groupMember; } diff --git a/services/static-webserver/client/source/class/osparc/store/Store.js b/services/static-webserver/client/source/class/osparc/store/Store.js index e560a643980..6b986a0a34d 100644 --- a/services/static-webserver/client/source/class/osparc/store/Store.js +++ b/services/static-webserver/client/source/class/osparc/store/Store.js @@ -55,6 +55,14 @@ qx.Class.define("osparc.store.Store", { check: "Object", init: {} }, + announcements: { + check: "Array", + init: [] + }, + maintenance: { + check: "Object", + init: {} + }, currentStudy: { check: "osparc.data.model.Study", init: null, @@ -111,10 +119,6 @@ qx.Class.define("osparc.store.Store", { init: [], event: "changeIterations" }, - maintenance: { - check: "Object", - init: {} - }, templates: { check: "Array", init: [] diff --git a/services/static-webserver/client/source/class/osparc/utils/Utils.js b/services/static-webserver/client/source/class/osparc/utils/Utils.js index 075db07763e..233198affb8 100644 --- a/services/static-webserver/client/source/class/osparc/utils/Utils.js +++ b/services/static-webserver/client/source/class/osparc/utils/Utils.js @@ -107,6 +107,11 @@ qx.Class.define("osparc.utils.Utils", { return newName; }, + isEmail: function(value) { + const reg = /^([A-Za-z0-9_\-.+])+@([A-Za-z0-9_\-.])+\.([A-Za-z]{2,})$/; + return reg.test(value); + }, + replaceTokens: function(str, key, value) { // `str` might be a a localized string, get the string first str = str.toString ? str.toString() : str; From 92ddd6c731457a3eff7c42c7527df32d7a3f76ff Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Wed, 11 Dec 2024 12:37:27 +0100 Subject: [PATCH 2/2] =?UTF-8?q?=20=F0=9F=90=9B=20Fixes=20web-api:=20issues?= =?UTF-8?q?=20on=20groups=20entrypoints=20(#6939)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api_schemas_webserver/groups.py | 23 +++++++++---------- .../src/models_library/groups.py | 3 +++ services/web/server/VERSION | 2 +- services/web/server/setup.cfg | 2 +- .../api/v0/openapi.yaml | 14 +++++++---- .../groups/_groups_api.py | 5 +--- .../01/groups/test_groups_handlers_crud.py | 12 ++++++++-- 7 files changed, 37 insertions(+), 24 deletions(-) diff --git a/packages/models-library/src/models_library/api_schemas_webserver/groups.py b/packages/models-library/src/models_library/api_schemas_webserver/groups.py index d595447c3d3..3b2b77199fb 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/groups.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/groups.py @@ -2,6 +2,7 @@ from typing import Annotated, Any, Self, TypeVar from common_library.basic_types import DEFAULT_FACTORY +from models_library.groups import EVERYONE_GROUP_ID from pydantic import ( AnyHttpUrl, AnyUrl, @@ -14,16 +15,16 @@ model_validator, ) -from ..basic_types import IDStr from ..emails import LowerCaseEmailStr from ..groups import ( AccessRightsDict, Group, + GroupID, GroupMember, StandardGroupCreate, StandardGroupUpdate, ) -from ..users import UserID +from ..users import UserID, UserNameID from ..utils.common_validators import create__check_only_one_is_set__root_validator from ._base import InputSchema, OutputSchema @@ -55,7 +56,7 @@ class GroupAccessRights(BaseModel): class GroupGet(OutputSchema): - gid: int = Field(..., description="the group ID") + gid: GroupID = Field(..., description="the group ID") label: str = Field(..., description="the group name") description: str = Field(..., description="the group description") thumbnail: AnyUrl | None = Field( @@ -114,7 +115,7 @@ def from_model(cls, group: Group, access_rights: AccessRightsDict) -> Self: "accessRights": {"read": True, "write": False, "delete": False}, }, { - "gid": "0", + "gid": "1", "label": "All", "description": "Open to all users", "accessRights": {"read": True, "write": True, "delete": True}, @@ -214,7 +215,7 @@ class MyGroupsGet(OutputSchema): }, ], "all": { - "gid": "0", + "gid": EVERYONE_GROUP_ID, "label": "All", "description": "Open to all users", "accessRights": {"read": True, "write": False, "delete": False}, @@ -228,13 +229,11 @@ class GroupUserGet(BaseModel): # OutputSchema # Identifiers - id: Annotated[ - str | None, Field(description="the user id", coerce_numbers_to_str=True) - ] = None - user_name: Annotated[IDStr, Field(alias="userName")] + id: Annotated[UserID | None, Field(description="the user's id")] = None + user_name: Annotated[UserNameID, Field(alias="userName")] gid: Annotated[ - str | None, - Field(description="the user primary gid", coerce_numbers_to_str=True), + GroupID | None, + Field(description="the user primary gid"), ] = None # Private Profile @@ -296,7 +295,7 @@ class GroupUserAdd(InputSchema): """ uid: UserID | None = None - user_name: Annotated[IDStr | None, Field(alias="userName")] = None + user_name: Annotated[UserNameID | None, Field(alias="userName")] = None email: Annotated[ LowerCaseEmailStr | None, Field( diff --git a/packages/models-library/src/models_library/groups.py b/packages/models-library/src/models_library/groups.py index e79928574a6..368f01523ea 100644 --- a/packages/models-library/src/models_library/groups.py +++ b/packages/models-library/src/models_library/groups.py @@ -13,6 +13,9 @@ EVERYONE_GROUP_ID: Final[int] = 1 +__all__: tuple[str, ...] = ("GroupID",) + + class GroupTypeInModel(str, enum.Enum): """ standard: standard group, e.g. any group that is not a primary group or special group such as the everyone group diff --git a/services/web/server/VERSION b/services/web/server/VERSION index 5c4503b7043..564edf82ddf 100644 --- a/services/web/server/VERSION +++ b/services/web/server/VERSION @@ -1 +1 @@ -0.49.0 +0.50.0 diff --git a/services/web/server/setup.cfg b/services/web/server/setup.cfg index 0e40e2535ee..9a74fe46b3d 100644 --- a/services/web/server/setup.cfg +++ b/services/web/server/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.49.0 +current_version = 0.50.0 commit = True message = services/webserver api version: {current_version} → {new_version} tag = False diff --git a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml index fe6f65828b9..d27cbd9537e 100644 --- a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml +++ b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml @@ -2,7 +2,7 @@ openapi: 3.1.0 info: title: simcore-service-webserver description: Main service with an interface (http-API & websockets) to the web front-end - version: 0.49.0 + version: 0.50.0 servers: - url: '' description: webserver @@ -10033,8 +10033,10 @@ components: properties: gid: type: integer + exclusiveMinimum: true title: Gid description: the group ID + minimum: 0 label: type: string title: Label @@ -10116,10 +10118,12 @@ components: properties: id: anyOf: - - type: string + - type: integer + exclusiveMinimum: true + minimum: 0 - type: 'null' title: Id - description: the user id + description: the user's id userName: type: string maxLength: 100 @@ -10127,7 +10131,9 @@ components: title: Username gid: anyOf: - - type: string + - type: integer + exclusiveMinimum: true + minimum: 0 - type: 'null' title: Gid description: the user primary gid diff --git a/services/web/server/src/simcore_service_webserver/groups/_groups_api.py b/services/web/server/src/simcore_service_webserver/groups/_groups_api.py index 9b9e712df54..18491ec60bf 100644 --- a/services/web/server/src/simcore_service_webserver/groups/_groups_api.py +++ b/services/web/server/src/simcore_service_webserver/groups/_groups_api.py @@ -258,14 +258,11 @@ async def add_user_in_group( ) new_by_user_id = user.id - if not new_by_user_id: - msg = "Missing new user in arguments" - raise GroupsError(msg=msg) - return await _groups_db.add_new_user_in_group( app, user_id=user_id, group_id=group_id, new_user_id=new_by_user_id, + new_user_name=new_by_user_name, access_rights=access_rights, ) diff --git a/services/web/server/tests/unit/with_dbs/01/groups/test_groups_handlers_crud.py b/services/web/server/tests/unit/with_dbs/01/groups/test_groups_handlers_crud.py index 5adaf33d9af..684f8726089 100644 --- a/services/web/server/tests/unit/with_dbs/01/groups/test_groups_handlers_crud.py +++ b/services/web/server/tests/unit/with_dbs/01/groups/test_groups_handlers_crud.py @@ -85,7 +85,7 @@ async def test_list_user_groups_and_try_modify_organizations( key=by_gid, ) == sorted(standard_groups, key=by_gid) - for group in standard_groups: + for i, group in enumerate(standard_groups): # try to delete a group url = client.app.router["delete_group"].url_for(gid=f"{group['gid']}") response = await client.delete(f"{url}") @@ -93,7 +93,15 @@ async def test_list_user_groups_and_try_modify_organizations( # try to add some user in the group url = client.app.router["add_group_user"].url_for(gid=f"{group['gid']}") - response = await client.post(f"{url}", json={"uid": logged_user["id"]}) + + if i % 2 == 0: + # by user-id + params = {"uid": logged_user["id"]} + else: + # by user name + params = {"userName": logged_user["name"]} + + response = await client.post(f"{url}", json=params) await assert_status(response, status.HTTP_403_FORBIDDEN) # try to modify the user in the group