-
Notifications
You must be signed in to change notification settings - Fork 0
Decline Project Invite - Service #264
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
16856fe
b7f5e49
3f43096
1c07ab9
ca7421b
171b927
0884ed2
d189c1d
fcb4dd8
52eece7
fbbeb9a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -65,15 +65,16 @@ export default class Project { | |
| let message = `You have been invited to the TPEN project ${projectTitle}. | ||
| View project <a href='${process.env.TPENINTERFACES}project?projectID=${this.data._id}'>here</a>.` | ||
| if (user) { | ||
| // FIXME this does not have the functionality of an 'invite'. | ||
| await this.inviteExistingTPENUser(user._id, roles) | ||
| } | ||
| else { | ||
| const inviteData = await this.inviteNewTPENUser(email, roles) | ||
| const returnTo = encodeURIComponent(`${process.env.TPENINTERFACES}project?projectID=${this.data._id}&inviteCode=${inviteData.tpenUserID}`) | ||
| // Signup starting at the TPEN3 public site | ||
| const signup = `${process.env.TPENTHREE}login?inviteCode=${inviteData.tpenUserID}&returnTo=${returnTo}` | ||
| // TODO decline endpoint in TPEN Services | ||
| const decline = `${process.env.TPENINTERFACES}project/decline?inviteCode=${inviteData.tpenUserID}&groupID=${inviteData.tpenGroupID}` | ||
| // Decline endpoint in TPEN Services | ||
| const decline = `${process.env.TPENINTERFACES}project/decline?email=${encodeURIComponent(email)}&user=${inviteData.tpenUserID}&project=${this.data._id}&projectTitle=${encodeURIComponent(projectTitle)}` | ||
| message += ` | ||
| <p> | ||
| Click the button below to get started with your project</p> | ||
|
|
@@ -138,12 +139,19 @@ export default class Project { | |
| return roles | ||
| } | ||
|
|
||
| /** | ||
| * Invite an existing TPEN3 User to the project. | ||
| * FIXME this does not have the functionality of an 'invite'. The User is added to the project. | ||
| * There is no step for them to accept or decline. | ||
| */ | ||
| async inviteExistingTPENUser(userId, roles) { | ||
| const group = new Group(this.data.group) | ||
| await group.addMember(userId, roles) | ||
| await this.addMember(userId, roles) | ||
| return this | ||
| } | ||
|
|
||
| /** | ||
| * Add a new temporary user to the users collection and send the invite E-mail. | ||
| */ | ||
| async inviteNewTPENUser(email, roles) { | ||
| const user = new User() | ||
| const inviteCode = user._id | ||
|
|
@@ -152,15 +160,44 @@ export default class Project { | |
| const _sub = `temp-${user._id}` // This is a temporary sub for the user until they verify their email | ||
| user.data = { email, _sub, profile, agent, inviteCode } | ||
| await user.save() | ||
| // FIXME this does not have the functionality of an 'invite'. | ||
| await this.inviteExistingTPENUser(user._id, roles) | ||
| return { "tpenUserID":user._id, "tpenGroupID":this.data.group } | ||
| } | ||
|
|
||
| /** | ||
| * Add a member to the Project Group. | ||
| * | ||
| * @param userId The User/member _id to add to the Group. | ||
| */ | ||
| async addMember(userId, roles) { | ||
| try { | ||
| const group = new Group(this.data.group) | ||
| await group.addMember(userId, roles) | ||
| } catch (error) { | ||
| throw { | ||
| status: error.status || 500, | ||
| message: error.message || "An error occurred while adding the member." | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
||
| /** | ||
| * Remove a member from the Project Group. | ||
| * If the member is an invitee (temporary) User, delete that User from the db. | ||
| * | ||
| * @param userId The User/member _id to remove from the Group and perhaps delete from the db. | ||
| */ | ||
| async removeMember(userId) { | ||
| try { | ||
| const group = new Group(this.data.group) | ||
| await group.removeMember(userId) | ||
| await group.update() | ||
| // Don't leave orphaned invitees in the db. | ||
| const member = new User(userId) | ||
| const memberData = await member.getSelf() | ||
| if(memberData?.inviteCode) member.delete() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a member with an inviteCode has not successfully joined and accepted yet. This makes sense. |
||
| return this | ||
| } catch (error) { | ||
| throw { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,12 +11,14 @@ import hotkeysRouter from "./hotkeysRouter.js" | |
| import metadataRouter from "./metadataRouter.js" | ||
| import projectToolsRouter from "./projectToolsRouter.js" | ||
| import memberUpgradeRouter from "./memberUpgradeRouter.js" | ||
| import memberDeclineInviteRouter from "./memberDeclineInviteRouter.js" | ||
|
|
||
| const router = express.Router({ mergeParams: true }) | ||
| router.use(cors(common_cors)) | ||
|
|
||
| // Use split routers | ||
| router.use(memberUpgradeRouter) // Contains unauthenticated route! | ||
| router.use(memberDeclineInviteRouter) // Contains unauthenticated route! | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these comments are odd. |
||
| router.use(projectCreateRouter) | ||
| router.use(import28Router) | ||
| router.use(projectReadRouter) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import express from "express" | ||
| import { validateID, respondWithError } from "../utilities/shared.js" | ||
| import Project from "../classes/Project/Project.js" | ||
| import User from "../classes/User/User.js" | ||
|
|
||
| const router = express.Router({ mergeParams: true }) | ||
|
|
||
| /** | ||
| * A user is declining from the E-mail they recieved. It is unauthenticated. | ||
| * Their member entry should be removed from the Group. | ||
| * Their temporary user should be removed from the db. | ||
| * | ||
| * @param projectId - The project which contains the temporary TPEN3 User as a member. | ||
| * @param collaboratorId - The temporary TPEN3 User declining the invitation. | ||
| */ | ||
| router.route("/:projectId/collaborator/:collaboratorId/decline").get(async (req, res) => { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I really like this organization |
||
| const { projectId, collaboratorId } = req.params | ||
| if (!projectId || !collaboratorId) return respondWithError(res, 400, "Not all data was provided.") | ||
| try { | ||
| const project = await new Project(projectId) | ||
| const projectData = await project.loadProject() | ||
| if (!projectData) return respondWithError(res, 404, "Project does not exist or the project id is invalid.") | ||
| const invitedUser = new User(collaboratorId) | ||
| const userData = await invitedUser.getSelf() | ||
| if (!userData?.profile) return respondWithError(res, 404, "This user has already declined or the user id is invalid.") | ||
| if (!userData?.inviteCode) return respondWithError(res, 400, "This user has already accepted the invitation.") | ||
| await project.removeMember(collaboratorId) | ||
| const name = userData.email ?? userData.profile.displayName ?? collaboratorId | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. getting this |
||
| res.status(200).send(`User '${name}' successfully declined the invitation.`) | ||
| } catch (error) { | ||
| return respondWithError( | ||
| res, | ||
| error.status || error.code || 500, | ||
| error.message ?? "There was an error declining the invitation." | ||
| ) | ||
| } | ||
| }).all((_, res) => { | ||
| respondWithError(res, 405, "Improper request method. Use GET instead") | ||
| }) | ||
|
|
||
| export default router | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,20 +28,20 @@ router.route("/:projectId/collaborator/:collaboratorId/agent/:agentId").get(asyn | |
| const tempData = await tempUser.getSelf() | ||
| if(!tempData?.profile) return respondWithError(res, 404, "Temporary user does not exist") | ||
| if(!tempData?.inviteCode) return respondWithError(res, 400, "Temporary user provided is not a temporary user.") | ||
| const project = await new Project(projectId).loadProject() | ||
| if(!project) return respondWithError(res, 404, "Project does not exist.") | ||
| const group = new Group(project.group) | ||
| const project = new Project(projectId) | ||
| const projectData = await project.loadProject() | ||
| if(!projectData) return respondWithError(res, 404, "Project does not exist.") | ||
| const group = new Group(projectData.group) | ||
| let tempRoles = await group.getMemberRoles(tempData._id) | ||
| if(!tempRoles) tempRoles = {"VIEWER":[]} | ||
| group.addMember(agentId, Object.keys(tempRoles)) | ||
| await project.addMember(agentId, Object.keys(tempRoles)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't love using the project.addMember here when you already have the Group. |
||
| try { | ||
| group.removeMember(tempData._id) | ||
| // This will also delete the temporary User from the users collection. | ||
| await project.removeMember(tempData._id) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This abstraction makes more sense on the client, but since it is just a kind of alias, I can accept it |
||
| } | ||
| catch (err) { | ||
| // keep going. | ||
| } | ||
| await group.update() | ||
| tempUser.delete() | ||
| res.status(200).send(`Temporary user '${collaboratorId}' upgraded to user '${agentId}'.`) | ||
| } catch (error) { | ||
| return respondWithError( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This only adds the catch and is only used once. It's a bit odd, but it looks fine