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