Thank you for taking the time to contribute. Every type of contribution is welcome.
The following is a set of guidelines for contributing to this project. These are mostly guidelines, not rules. Also, feel free to propose changes to this document in a pull request.
- How Can I Contribute?
- Project setup
- Development
- Committing and pushing changes
- Style-guides
- Help needed
- Reporting Bugs
- Suggesting Enhancements
- Pull Requests
To report bugs or suggest enhancements, please use the issues page.
To make pull requests:
- setup the project locally
- make your changes; Please try to follow the development guidelines while making your changes
- commit and push the changes
- submit the pull request
- Fork the repo to your GitHub account
- Clone the repo:
git clone https://github.com/<your-github-username>/express-user-manager
- Navigate to the repo's directory:
cd express-user-manager
- Run
npm install
to install dependencies - Create a branch for your PR with
git checkout -b pr/your-branch-name
Tip: Keep your
master
branch pointing at the original repository while still making pull requests from branches on your fork. To do this, run:git remote add upstream https://github.com/simplymichael/express-user-manager.git git fetch upstream git branch --set-upstream-to=upstream/master master
This does the following:
adds the original repository as a "remote" called "upstream"
fetches the git information from that remote
sets your local
master
branch to pull the latest changes from the upstream master branch whenever you rungit pull
.Now you can make all of your pull request branches based on this local
master
branch.Whenever you want to update your local
master
branch, do a regulargit pull
. You can push the updated changes to your remote origin master by runninggit push
.
To run the tests,
- Ensure you have a MongoDB server running on the default port (27017). (See Setting up test databases for an example of how to do this)
- Ensure you have a MySQL server running on the default port (3306) and that a table named users exists in the database. (See Setting up test databases for an example of how to do this)
cd
into the express-user-manager directory.- Copy the .env.example file to .env and edit the values for the following variables:
DB_HOST=localhost
DB_DBNAME=users
DB_DEBUG=false
EXIT_ON_DB_CONNECT_FAIL=true
SESSION_SECRET=secret
AUTH_TOKEN_SECRET=secret
AUTH_TOKEN_EXPIRY="60 * 60 * 24"
PASSWORD_MIN_LENGTH=6
PASSWORD_MAX_LENGTH=20
DISALLOWED_PASSWORDS=password,passw0Rd,secret,Passw0rd,Password123
- Run all tests:
npm test
- Run all tests with coverage report:
npm run test:coverage
- Run only the database tests:
npm run test:db:all
- Run only the end-to-end tests:
npm run test:e2e:all
Notes:
-
All non-database-engine-specific tests run the tests on the following databases: in-memory, MongoDB, MySQL, SQLite.
-
You can run database-engine-specific tests by replacing the
:all
postfix with:DB_ENGINE
in the database and end-to-end tests. For example:npm run test:db:memory
will run the database tests using only the in-memory database.npm run test:e2e:mongoose
will run the end-to-end tests using only the mongoose database.
The following post-fixes are supported:
:all
,:memory
,:mongoose
,:mysql
,:sqlite
.
You can run end-to-end tests on the routes using Postman or cURL. To do this,
- Start the built-in server. You can start the built-in server in one of two ways:
- Follow the steps listed in the Usage as a standalone server section to start the server.
- Run
npm
scripts to start the server:cd
into the express-user-manager directory.- If you are using a MongoDB or MySQL server, ensure the server is up and running on the port you specified.
- run
npm run serve
to start the server - run
npm run serve:watch
to start the server in watch mode. This watches thesrc/
directory's files and automatically restarts the server with the latest changes when you edit or update the source files.
- Make http requests to the (default) API routes using Postman or cURL.
- Setup a MongoDB database:
- Create the container:
docker run -d -it --rm --name mongodb -p 27017:27017 mongo:4.4.4
- You can create volume mappings between your OS and the container using the
-v
flag:-v path/on/your/host/system:/etc/mongo
- Create the container:
- Setup a MySQL database:
- Create the container:
docker run -d -it --rm --name mysql -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql:5.7 --default-authentication-plugin=mysql_native_password
- Log into the container and create the users database:
docker exec -it mysql mysql -h localhost -u root -e "CREATE DATABASE IF NOT EXISTS users"
- You can also create volume mappings between your OS and the container as follows:
-v path/on/your/host/system:/var/lib/mysql
- Create the container:
To see debug output on the console, set the DEBUG
environment variable to express-user-manager
before running the tests or starting the built-in server: set DEBUG=express-user-manager
To create a new database adapter, create a new file. Inside the file, define and export a class with the following methods:
constructor(emit, debug, toBoolean)
: The constructor automatically receives three arguments:emit
: a function for emitting events, used this way:this.emit(EVENT, DATA)
.debug
: a function for outputting debugging output when the DEBUG environment variable is set toexpress-user-manager
; This function should be used liberally to output events in the object's life-cycle.toBoolean
: a function to convert boolean-like expressions to boolean. It was created to help with parsing "boolean" values coming from environment variables. Examples:this.toBoolean("false")
evaluates tofalse
this.toBoolean("0")
evaluates tofalse
this.toBoolean(false)
evaluates tofalse
this.toBoolean(0)
evaluates tofalse
- All other conversions evaluate to
true
async connect(options)
:options
is an object with members:host
{string} db server hostport
{number} db server portuser
{string} db server usernamepass
{string} db user's passwordengine
{string} the database engine to use.- Possible values are:
memory, mariadb, mssql, mysql, postgres, sqlite
- This parameter is not required when using the
mongoose
adapter:express-user-manager.getDbAdapter('mongoose')
.
- Possible values are:
storagePath
{string} The storage location when theengine
is set topostgres
.- The value is combined with the
dbName
option to set the storage:${storagePath}/${dbName}.sqlite
- The value is combined with the
dbName
{string} the name of the database to connect todebug
{boolean | number(int | 0)} determines whether or not to show debugging output On successful connection, it should do two things:- emit a dbConnection event with the connection resource as the data:
this.emit('dbConnection', conn);
- return the connection resource:
return conn;
async disconnect()
: On disconnect success, it should emit a dbDisconnect event:this.emit('dbDisconnect');
async createUser(userData)
:userData
should be an object with members:firstname
{string}lastname
{string}username
{string}email
{string}password
{string}passwordConfirm
{string} On success, it should:- emit a createUser event with the new user's data:
this.emit('createUser', userData);
- return the new user's data:
return userData;
whereuserData
is an object:
return { id: USER_ID firstname: FNAME, lastname: LNAME, email: EMAIL, username: USERNAME, signupDate: DATE_CREATED }
async getUsers(options)
:options
is an object with the following optional members:firstname
{string} filter by users with matching firstnames (optional)lastname
{string} get users with matching lastnames (optional)page
{number} the page to return, for pagination purposes (optional, default 1)limit
{number} the number of results to return, for pagination purposes (optional, default 20)sort
{string} determines the sort order of returned users (optional) It should return an object with the following members:total
{number} the total number of users that match the specified firstname and/or lastname filterslength
{number} the number of results returned for the current page and limitusers
{array} of objects representing the list of returned users that match the search term
async searchUsers(options)
:options
is an object with the following members:query
{string} the search term (required)by
{string} whether to search by firstname, lastname, username, email (optional, default searches by all)page
{number} the page to return, for pagination purposes (optional, default 1)limit
{number} the number of results to return, for pagination purposes (optional, default 20)sort
{string} determines the sort order of returned users (optional) It should return an object with the following members:total
{number} the total number of users that match the specified firstname and/or lastname filterslength
{number} the number of results returned for the current page and limitusers
{array} of objects representing the list of returned users that match the search term
async findByEmail(email)
: should return a user object:return { id: USER_ID, firstname: FNAME, lastname: LNAME, fullname: USER_FULLNAME, email: EMAIL, username: USERNAME, signupDate: DATE_CREATED }
async findByUsername(username)
: should return a user object:return { id: USER_ID, firstname: FNAME, lastname: LNAME, fullname: USER_FULLNAME, email: EMAIL, username: USERNAME, signupDate: DATE_CREATED }
async findById(userId)
: should return a user object:return { id: USER_ID, firstname: FNAME, lastname: LNAME, fullname: USER_FULLNAME, email: EMAIL, username: USERNAME, signupDate: DATE_CREATED }
async updateUser(userId, updateData)
:updateData
should be an object with members:firstname
{string}lastname
{string}username
{string}email
{string} On success, it should:- emit an updateUserSuccess event with the updated user's data:
this.emit('updateUserSuccess', userData);
- return the new user's data:
return userData;
whereuserData
is an object:
return { id: USER_ID firstname: FNAME, lastname: LNAME, fullname: USER_FULLNAME, email: EMAIL, username: USERNAME, signupDate: DATE_CREATED }
async deleteUser(userId)
Save the file in one of two ways:
- as {adapterName}.js inside the
src/databases/adapters
directory - as
index.js
inside a directory name {adapterName}, then place the directory in thesrc/databases/adapters
directory
This project follows the Conventional Commits Specification and uses ESLint for linting.
Before committing your changes, run npm run lint:fix
to check and automatically fix linting errors.
If there are linting errors that cannot be automatically fixed, they are highlighted, so that you can manually fix them.
To commit your changes, run npm run commit
. This will:
- generate conventional commit messages using commitizen and cz-conventional-changelog
- check to make sure there are no linting errors
- run the tests to make sure there are no breaking changes
- check that the minimum code-coverage threshold is attained
- apply the commit
Once everything checks out and the commit is applied,
you can then push your changes by running git push -u remote pr/your-branch-name
.
You can keep making and pushing updates to your pull request branch until you feel ready to have your changes merged into the main project.
When you are ready to have your changes merged, you can then open a pull request.
- Use the present tense ("Add feature" not "Added feature")
- Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
- Limit the first line (subject line) to 72 characters or less
- Reference issues and pull requests liberally after the first line
- Consider starting the commit message with an applicable emoji:
- 🎨
:art:
when improving the format/structure of the code - 🐎
:racehorse:
when improving performance - 🚱
:non-potable_water:
when plugging memory leaks - 📝
:memo:
when writing docs - 🐧
:penguin:
when fixing something on Linux - 🍎
:apple:
when fixing something on macOS - 🏁
:checkered_flag:
when fixing something on Windows - 🐛
:bug:
when fixing a bug - 🔥
:fire:
when removing code or files - 💚
:green_heart:
when fixing the CI build - ✅
:white_check_mark:
when adding tests - 🔒
:lock:
when dealing with security - ⬆️
:arrow_up:
when upgrading dependencies - ⬇️
:arrow_down:
when downgrading dependencies - 👕
:shirt:
when removing linter warnings
- 🎨
Please checkout the the issues page for any open issues.
Also, please watch the repo and respond to questions/bug reports/feature requests! Thanks!