This document lists all the expected behaviors for the APIs that compose the EZWallet application.
The examples regarding request parameters and request body content must be followed: all attributes must be named in the same ways as the examples. The examples regarding response data content define the necessary attributes that must be present in the objects returned by the various APIs, other additional attributes can be present but are not required.
In cases where the APIs return an error the correct structure to follow is:
res.status(errorCode).json({ error: "Error message" });The actual value of the error, message, and refreshedTokenMessage attributes, where required, does not matter as long as the attributes are included in the return object (any string is accepted).
Route parameters (where needed) cannot be empty, as not having them would define a new route, leading to a 404 error in Postman.
The functions that require Simple, User, and Admin authentication must have the necessary checks performed before any other check, the functions that require Group authentication must first check if the requested group, then check for authentication, and then perform any other additional check.
The registerAdmin function does not require any check on whether the user calling it is an authenticated Admin: if such checks were needed, an Admin would have to be created before calling the function, but the only way to create an Admin would be with the function itself, leading to a deadlock. The requirement on Admins being allowed to call the function is a logical one.
- Request Parameters: None
- Request Body Content: An object having attributes
username,emailandpassword- Example:
{username: "Mario", email: "mario.red@email.com", password: "securePass"}
- Example:
- Response
dataContent: A message confirming successful insertion- Example:
res.status(200).json({data: {message: "User added successfully"}})
- Example:
- Returns a 400 error if the request body does not contain all the necessary attributes
- Returns a 400 error if at least one of the parameters in the request body is an empty string
- Returns a 400 error if the email in the request body is not in a valid email format
- Returns a 400 error if the username in the request body identifies an already existing user
- Returns a 400 error if the email in the request body identifies an already existing user
- Request Parameters: None
- Request Body Content: An object having attributes
username,emailandpassword- Example:
{username: "admin", email: "admin@email.com", password: "securePass"}
- Example:
- Response
dataContent: A message confirming successful insertion- Example:
res.status(200).json({data: {message: "User added successfully"}})
- Example:
- Returns a 400 error if the request body does not contain all the necessary attributes
- Returns a 400 error if at least one of the parameters in the request body is an empty string
- Returns a 400 error if the email in the request body is not in a valid email format
- Returns a 400 error if the username in the request body identifies an already existing user
- Returns a 400 error if the email in the request body identifies an already existing user
- Request Parameters: None
- Request Body Content: An object having attributes
emailandpassword- Example:
{email: "mario.red@email.com", password: "securePass"}
- Example:
- Response
dataContent: An object with the created accessToken and refreshToken- Example:
res.status(200).json({data: {accessToken: accessToken, refreshToken: refreshToken}})
- Example:
- Returns a 400 error if the request body does not contain all the necessary attributes
- Returns a 400 error if at least one of the parameters in the request body is an empty string
- Returns a 400 error if the email in the request body is not in a valid email format
- Returns a 400 error if the email in the request body does not identify a user in the database
- Returns a 400 error if the supplied password does not match with the one in the database
- Request Parameters: None
- Request Body Content: None
- Response
dataContent: A message confirming successful logout- Example:
res.status(200).json({data: {message: "User logged out"}})
- Example:
- Returns a 400 error if the request does not have a refresh token in the cookies
- Returns a 400 error if the refresh token in the request's cookies does not represent a user in the database
- Request Parameters: None
- Request Body Content: An object having attributes
typeandcolor- Example:
{type: "food", color: "red"}
- Example:
- Response
dataContent: An object having attributestypeandcolor- Example:
res.status(200).json({data: {type: "food", color: "red"}, refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- Returns a 400 error if the request body does not contain all the necessary attributes
- Returns a 400 error if at least one of the parameters in the request body is an empty string
- Returns a 400 error if the type of category passed in the request body represents an already existing category in the database
- Returns a 401 error if called by an authenticated user who is not an admin (authType = Admin)
- Request Parameters: A string equal to the
typeof the category that must be edited- Example:
api/categories/food
- Example:
- Request Body Content: An object having attributes
typeandcolorequal to the new values to assign to the category- Example:
{type: "Food", color: "yellow"}
- Example:
- Response
dataContent: An object with parametermessagethat confirms successful editing and a parametercountthat is equal to the count of transactions whose category was changed with the new type- Example:
res.status(200).json({data: {message: "Category edited successfully", count: 2}, refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- In case any of the following errors apply then the category is not updated, and transactions are not changed
- Returns a 400 error if the request body does not contain all the necessary attributes
- Returns a 400 error if at least one of the parameters in the request body is an empty string
- Returns a 400 error if the type of category passed as a route parameter does not represent a category in the database
- Returns a 400 error if the type of category passed in the request body as the new type represents an already existing category in the database and that category is not the same as the requested one
- Returns a 401 error if called by an authenticated user who is not an admin (authType = Admin)
- Request Parameters: None
- Request Body Content: An array of strings that lists the
typesof the categories to be deleted- Example:
{types: ["health"]}
- Example:
- Response
dataContent: An object with an attributemessagethat confirms successful deletion and an attributecountthat specifies the number of transactions that have had their category type changed- Example:
res.status(200).json({data: {message: "Categories deleted", count: 1}, refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- Given N = categories in the database and T = categories to delete:
- If N > T then all transactions with a category to delete must have their category set to the oldest category that is not in T
- If N = T then the oldest created category cannot be deleted and all transactions must have their category set to that category
- In case any of the following errors apply then no category is deleted
- Returns a 400 error if the request body does not contain all the necessary attributes
- Returns a 400 error if called when there is only one category in the database
- Returns a 400 error if at least one of the types in the array is an empty string
- Returns a 400 error if the array passed in the request body is empty
- Returns a 400 error if at least one of the types in the array does not represent a category in the database
- Returns a 401 error if called by an authenticated user who is not an admin (authType = Admin)
- Request Parameters: None
- Request Body Content: None
- Response
dataContent: An array of objects, each one having attributestypeandcolor- Example:
res.status(200).json({data: [{type: "food", color: "red"}, {type: "health", color: "green"}], refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- Returns a 401 error if called by a user who is not authenticated (authType = Simple)
- Request Parameters: A string equal to the
usernameof the involved user- Example:
/api/users/Mario/transactions
- Example:
- Request Body Content: An object having attributes
username,typeandamount- Example:
{username: "Mario", amount: 100, type: "food"}
- Example:
- Response
dataContent: An object having attributesusername,type,amountanddate- Example:
res.status(200).json({data: {username: "Mario", amount: 100, type: "food", date: "2023-05-19T00:00:00"}, refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- Returns a 400 error if the request body does not contain all the necessary attributes
- Returns a 400 error if at least one of the parameters in the request body is an empty string
- Returns a 400 error if the type of category passed in the request body does not represent a category in the database
- Returns a 400 error if the username passed in the request body is not equal to the one passed as a route parameter
- Returns a 400 error if the username passed in the request body does not represent a user in the database
- Returns a 400 error if the username passed as a route parameter does not represent a user in the database
- Returns a 400 error if the amount passed in the request body cannot be parsed as a floating value (negative numbers are accepted)
- Returns a 401 error if called by an authenticated user who is not the same user as the one in the route parameter (authType = User)
- Request Parameters: None
- Request Body Content: None
- Response
dataContent: An array of objects, each one having attributesusername,type,amount,dateandcolor- Example:
res.status(200).json({data: [{username: "Mario", amount: 100, type: "food", date: "2023-05-19T00:00:00", color: "red"}, {username: "Mario", amount: 70, type: "health", date: "2023-05-19T10:00:00", color: "green"}, {username: "Luigi", amount: 20, type: "food", date: "2023-05-19T10:00:00", color: "red"} ], refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- Returns a 401 error if called by an authenticated user who is not an admin (authType = Admin)
- Request Parameters: A string equal to the
usernameof the involved user- Example:
/api/users/Mario/transactions(user route) - Example:
/api/transactions/users/Mario(admin route)
- Example:
- Request Body Content: None
- Response
dataContent: An array of objects, each one having attributesusername,type,amount,dateandcolor- Example:
res.status(200).json({data: [{username: "Mario", amount: 100, type: "food", date: "2023-05-19T00:00:00", color: "red"}, {username: "Mario", amount: 70, type: "health", date: "2023-05-19T10:00:00", color: "green"} ] refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- Returns a 400 error if the username passed as a route parameter does not represent a user in the database
- Returns a 401 error if called by an authenticated user who is not the same user as the one in the route (authType = User) if the route is
/api/users/:username/transactions - Returns a 401 error if called by an authenticated user who is not an admin (authType = Admin) if the route is
/api/transactions/users/:username - Can be filtered by date and amount if the necessary query parameters are present and if the route is
/api/users/:username/transactions
- The behavior defined below applies only for the specified route
- Request Parameters: A string equal to the
usernameof the involved user, a string equal to the requestedcategory- Example:
/api/users/Mario/transactions/category/food(user route) - Example:
/api/transactions/users/Mario/category/food(admin route)
- Example:
- Request Body Content: None
- Response
dataContent: An array of objects, each one having attributesusername,type,amount,dateandcolor, filtered so thattypeis the same for all objects- Example:
res.status(200).json({data: [{username: "Mario", amount: 100, type: "food", date: "2023-05-19T00:00:00", color: "red"} ] refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- Returns a 400 error if the username passed as a route parameter does not represent a user in the database
- Returns a 400 error if the category passed as a route parameter does not represent a category in the database
- Returns a 401 error if called by an authenticated user who is not the same user as the one in the route (authType = User) if the route is
/api/users/:username/transactions/category/:category - Returns a 401 error if called by an authenticated user who is not an admin (authType = Admin) if the route is
/api/transactions/users/:username/category/:category
- Request Parameters: A string equal to the
nameof the requested group- Example:
/api/groups/Family/transactions(user route) - Example:
/api/transactions/groups/Family(admin route)
- Example:
- Request Body Content: None
- Response
dataContent: An array of objects, each one having attributesusername,type,amount,dateandcolor- Example:
res.status(200).json({data: [{username: "Mario", amount: 100, type: "food", date: "2023-05-19T00:00:00", color: "red"}, {username: "Mario", amount: 70, type: "health", date: "2023-05-19T10:00:00", color: "green"}, {username: "Luigi", amount: 20, type: "food", date: "2023-05-19T10:00:00", color: "red"} ] refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- Returns a 400 error if the group name passed as a route parameter does not represent a group in the database
- Returns a 401 error if called by an authenticated user who is not part of the group (authType = Group) if the route is
/api/groups/:name/transactions - Returns a 401 error if called by an authenticated user who is not an admin (authType = Admin) if the route is
/api/transactions/groups/:name
- Request Parameters: A string equal to the
nameof the requested group, a string equal to the requestedcategory- Example:
/api/groups/Family/transactions/category/food(user route) - Example:
/api/transactions/groups/Family/category/food(admin route)
- Example:
- Request Body Content: None
- Response
dataContent: An array of objects, each one having attributesusername,type,amount,dateandcolor, filtered so thattypeis the same for all objects.- Example:
res.status(200).json({data: [{username: "Mario", amount: 100, type: "food", date: "2023-05-19T00:00:00", color: "red"}, {username: "Luigi", amount: 20, type: "food", date: "2023-05-19T10:00:00", color: "red"} ] refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- Returns a 400 error if the group name passed as a route parameter does not represent a group in the database
- Returns a 400 error if the category passed as a route parameter does not represent a category in the database
- Returns a 401 error if called by an authenticated user who is not part of the group (authType = Group) if the route is
/api/groups/:name/transactions/category/:category - Returns a 401 error if called by an authenticated user who is not an admin (authType = Admin) if the route is
/api/transactions/groups/:name/category/:category
- Request Parameters: A string equal to the
usernameof the involved user- Example:
/api/users/Mario/transactions
- Example:
- Request Body Content: The
_idof the transaction to be deleted- Example:
{_id: "6hjkohgfc8nvu786"}
- Example:
- Response
dataContent: A string indicating successful deletion of the transaction- Example:
res.status(200).json({data: {message: "Transaction deleted"}, refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- Returns a 400 error if the request body does not contain all the necessary attributes
- Returns a 400 error if the
_idin the request body is an empty string - Returns a 400 error if the username passed as a route parameter does not represent a user in the database
- Returns a 400 error if the
_idin the request body does not represent a transaction in the database - Returns a 400 error if the
_idin the request body represents a transaction made by a different user than the one in the route - Returns a 401 error if called by an authenticated user who is not the same user as the one in the route (authType = User)
- Request Parameters: None
- Request Body Content: An array of strings that lists the
_idsof the transactions to be deleted- Example:
{_ids: ["6hjkohgfc8nvu786"]}
- Example:
- Response
dataContent: A message confirming successful deletion- Example:
res.status(200).json({data: {message: "Transactions deleted"}, refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- In case any of the following errors apply then no transaction is deleted
- Returns a 400 error if the request body does not contain all the necessary attributes
- Returns a 400 error if at least one of the ids in the array is an empty string
- Returns a 400 error if at least one of the ids in the array does not represent a transaction in the database
- Returns a 401 error if called by an authenticated user who is not an admin (authType = Admin)
- Request Parameters: None
- Request Body Content: None
- Response
dataContent: An array of objects, each one having attributesusername,emailandrole- Example:
res.status(200).json({data: [{username: "Mario", email: "mario.red@email.com", role: "Regular"}, {username: "Luigi", email: "luigi.red@email.com", role: "Regular"}, {username: "admin", email: "admin@email.com", role: "Regular"} ], refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- Returns a 401 error if called by an authenticated user who is not an admin (authType = Admin)
- Request Parameters: A string equal to the
usernameof the involved user- Example:
/api/users/Mario
- Example:
- Request Body Content: None
- Response
dataContent: An object having attributesusername,emailandrole.- Example:
res.status(200).json({data: {username: "Mario", email: "mario.red@email.com", role: "Regular"}, refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- Returns a 400 error if the username passed as the route parameter does not represent a user in the database
- Returns a 401 error if called by an authenticated user who is neither the same user as the one in the route parameter (authType = User) nor an admin (authType = Admin)
- Request Parameters: None
- Request request body Content: An object having a string attribute for the
nameof the group and an array that lists all thememberEmails- Example:
{name: "Family", memberEmails: ["mario.red@email.com", "luigi.red@email.com"]}
- Example:
- Response
dataContent: An object having an attributegroup(this object must have a string attribute for thenameof the created group and an array for themembersof the group), an array that lists thealreadyInGroupmembers (members whose email is already present in a group) and an array that lists themembersNotFound(members whose email does not appear in the system)- Example:
res.status(200).json({data: {group: {name: "Family", members: [{email: "mario.red@email.com"}, {email: "luigi.red@email.com"}]}, membersNotFound: [], alreadyInGroup: []} refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- If the user who calls the API does not have their email in the list of emails then their email is added to the list of members
- Returns a 400 error if the request body does not contain all the necessary attributes
- Returns a 400 error if the group name passed in the request body is an empty string
- Returns a 400 error if the group name passed in the request body represents an already existing group in the database
- Returns a 400 error if all the provided emails (the ones in the array, the email of the user calling the function does not have to be considered in this case) represent users that are already in a group or do not exist in the database
- Returns a 400 error if the user who calls the API is already in a group
- Returns a 400 error if at least one of the member emails is not in a valid email format
- Returns a 400 error if at least one of the member emails is an empty string
- Returns a 401 error if called by a user who is not authenticated (authType = Simple)
- Request Parameters: None
- Request Body Content: None
- Response
dataContent: An array of objects, each one having a string attribute for thenameof the group and an array for themembersof the group- Example:
res.status(200).json({data: [{name: "Family", members: [{email: "mario.red@email.com"}, {email: "luigi.red@email.com"}]}] refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- Returns a 401 error if called by an authenticated user who is not an admin (authType = Admin)
- Request Parameters: A string equal to the
nameof the requested group- Example:
/api/groups/Family
- Example:
- Request Body Content: None
- Response
dataContent: An object having a string attribute for thenameof the group and an array for themembersof the group- Example:
res.status(200).json({data: {group: {name: "Family", members: [{email: "mario.red@email.com"}, {email: "luigi.red@email.com"}]}} refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- Returns a 400 error if the group name passed as a route parameter does not represent a group in the database
- Returns a 401 error if called by an authenticated user who is neither part of the group (authType = Group) nor an admin (authType = Admin)
- Request Parameters: A string equal to the
nameof the group- Example:
api/groups/Family/add(user route) - Example:
api/groups/Family/insert(admin route)
- Example:
- Request Body Content: An array of strings containing the
emailsof the members to add to the group- Example:
{emails: ["pietro.blue@email.com"]}
- Example:
- Response
dataContent: An object having an attributegroup(this object must have a string attribute for thenameof the created group and an array for themembersof the group, this array must include the new members as well as the old ones), an array that lists thealreadyInGroupmembers (members whose email is already present in a group) and an array that lists themembersNotFound(members whose email does not appear in the system)- Example:
res.status(200).json({data: {group: {name: "Family", members: [{email: "mario.red@email.com"}, {email: "luigi.red@email.com"}, {email: "pietro.blue@email.com"}]}, membersNotFound: [], alreadyInGroup: []} refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- In case any of the following errors apply then no user is added to the group
- Returns a 400 error if the request body does not contain all the necessary attributes
- Returns a 400 error if the group name passed as a route parameter does not represent a group in the database
- Returns a 400 error if all the provided emails represent users that are already in a group or do not exist in the database
- Returns a 400 error if at least one of the member emails is not in a valid email format
- Returns a 400 error if at least one of the member emails is an empty string
- Returns a 401 error if called by an authenticated user who is not part of the group (authType = Group) if the route is
api/groups/:name/add - Returns a 401 error if called by an authenticated user who is not an admin (authType = Admin) if the route is
api/groups/:name/insert
- Request Parameters: A string equal to the
nameof the group- Example:
api/groups/Family/remove(user route) - Example:
api/groups/Family/pull(admin route)
- Example:
- Request Body Content: An array of strings containing the
emailsof the members to remove from the group- Example:
{emails: ["pietro.blue@email.com"]}
- Example:
- Response
dataContent: An object having an attributegroup(this object must have a string attribute for thenameof the created group and an array for themembersof the group, this array must include only the remaining members), an array that lists thenotInGroupmembers (members whose email is not in the group) and an array that lists themembersNotFound(members whose email does not appear in the system)- Example:
res.status(200).json({data: {group: {name: "Family", members: [{email: "mario.red@email.com"}, {email: "luigi.red@email.com"}]}, membersNotFound: [], notInGroup: []} refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- The group must have at least one user after deleting, so given M = members of the group and N = emails to delete:
- if N >= M at least one member of the group cannot be deleted (the member that remains can be any member, there is no rule on which one it must be)
- In case any of the following errors apply then no user is removed from the group
- Returns a 400 error if the request body does not contain all the necessary attributes
- Returns a 400 error if the group name passed as a route parameter does not represent a group in the database
- Returns a 400 error if all the provided emails represent users that do not belong to the group or do not exist in the database
- Returns a 400 error if at least one of the emails is not in a valid email format
- Returns a 400 error if at least one of the emails is an empty string
- Returns a 400 error if the group contains only one member before deleting any user
- Returns a 401 error if called by an authenticated user who is not part of the group (authType = Group) if the route is
api/groups/:name/remove - Returns a 401 error if called by an authenticated user who is not an admin (authType = Admin) if the route is
api/groups/:name/pull
- Request Parameters: None
- Request Body Content: A string equal to the
emailof the user to be deleted- Example:
{email: "luigi.red@email.com"}
- Example:
- Response
dataContent: An object having an attribute that lists the number ofdeletedTransactionsand an attribute that specifies whether the user was alsodeletedFromGroupor not- Example:
res.status(200).json({data: {deletedTransactions: 1, deletedFromGroup: true}, refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- If the user is the last user of a group then the group is deleted as well
- Returns a 400 error if the request body does not contain all the necessary attributes
- Returns a 400 error if the email passed in the request body is an empty string
- Returns a 400 error if the email passed in the request body is not in correct email format
- Returns a 400 error if the email passed in the request body does not represent a user in the database
- Returns a 400 error if the email passed in the request body represents an admin
- Returns a 401 error if called by an authenticated user who is not an admin (authType = Admin)
- Request Parameters: None
- Request Body Content: A string equal to the
nameof the group to be deleted- Example:
{name: "Family"}
- Example:
- Response
dataContent: A message confirming successful deletion- Example:
res.status(200).json({data: {message: "Group deleted successfully"} , refreshedTokenMessage: res.locals.refreshedTokenMessage})
- Example:
- Returns a 400 error if the request body does not contain all the necessary attributes
- Returns a 400 error if the name passed in the request body is an empty string
- Returns a 400 error if the name passed in the request body does not represent a group in the database
- Returns a 401 error if called by an authenticated user who is not an admin (authType = Admin)
- Returns an object with a
dateattribute used for filtering mongoDB'saggregatequeries - The value of
dateis an object that depends on the query parameters:- If the query parameters include
fromthen it must include a$gteattribute that specifies the starting date as aDateobject in the format YYYY-MM-DDTHH:mm:ss- Example:
/api/users/Mario/transactions?from=2023-04-30=>{date: {$gte: 2023-04-30T00:00:00.000Z}}
- Example:
- If the query parameters include
upTothen it must include a$lteattribute that specifies the ending date as aDateobject in the format YYYY-MM-DDTHH:mm:ss- Example:
/api/users/Mario/transactions?upTo=2023-05-10=>{date: {$lte: 2023-05-10T23:59:59.999Z}}
- Example:
- If both
fromandupToare present then both$gteand$ltemust be included - If
dateis present then it must include both$gteand$lteattributes, these two attributes must specify the same date as aDateobject in the format YYYY-MM-DDTHH:mm:ss- Example:
/api/users/Mario/transactions?date=2023-05-10=>{date: {$gte: 2023-05-10T00:00:00.000Z, $lte: 2023-05-10T23:59:59.999Z}}
- Example:
- If there is no query parameter then it returns an empty object
- Example:
/api/users/Mario/transactions=>{}
- Example:
- If the query parameters include
- Throws an error if
dateis present in the query parameter together with at least one offromorupTo - Throws an error if the value of any of the three query parameters is not a string that represents a date in the format YYYY-MM-DD
- Verifies that the tokens present in the request's cookies allow access depending on the different criteria.
- Returns an object with a boolean
flagthat specifies whether access is granted or not and acausethat describes the reason behind failed authentication- Example:
{authorized: false, cause: "Unauthorized"}
- Example:
- Refreshes the
accessTokenif it has expired and therefreshTokenallows authentication; sets therefreshedTokenMessageto inform users that theaccessTokenmust be changed
- Returns an object with an
amountattribute used for filtering mongoDB'saggregatequeries - The value of
amountis an object that depends on the query parameters:- If the query parameters include
minthen it must include a$gteattribute that is an integer equal tomin- Example:
/api/users/Mario/transactions?min=10=> `{amount: {$gte: 10} }
- Example:
- If the query parameters include
minthen it must include a$lteattribute that is an integer equal tomax- Example:
/api/users/Mario/transactions?min=50=> `{amount: {$lte: 50} }
- Example:
- If both
minandmaxare present then both$gteand$ltemust be included - If neither is present then the function must return an empty object
- Example:
/api/users/Mario/transactions=>{}
- Example:
- If the query parameters include
- Throws an error if the value of any of the two query parameters is not a numerical value