Skip to content

Commit

Permalink
feat: issue19-24 setup and User Registration API
Browse files Browse the repository at this point in the history
  • Loading branch information
mtreacy002 committed Jun 6, 2020
1 parent ba92967 commit c41978c
Show file tree
Hide file tree
Showing 51 changed files with 3,728 additions and 0 deletions.
15 changes: 15 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FLASK_ENVIRONMENT_CONFIG = <dev-or-test-or-prod>
SECRET_KEY = <your-secret-key>
SECURITY_PASSWORD_SALT = <your-security-password-salt>
MAIL_DEFAULT_SENDER = <mail-default-sender>
MAIL_SERVER = <mail-server>
APP_MAIL_USERNAME = <app-mail-username>
APP_MAIL_PASSWORD = <app-mail-password>
MOCK_EMAIL = <True-or-False>
FLASK_APP=run.py
DB_TYPE=postgresql
DB_USERNAME= <db-username>
DB_PASSWORD= <db-password>
DB_ENDPOINT= <db-endpoint>
DB_NAME=bit_schema
DB_TEST_NAME=bit_schema_test
115 changes: 115 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/

# PyCharm project settings
.idea/

# vscode
.vscode/

*.db

# aws-eb
.elasticbeanstalk/
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: gunicorn run:application
Empty file added app/__init__.py
Empty file.
Empty file added app/api/__init__.py
Empty file.
21 changes: 21 additions & 0 deletions app/api/bit_extension.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from flask_restx import Api

api = Api(
title="Bridge In Tech API",
version="1.0",
description="API documentation for the backend of Bridge In Tech. \n \n"
+ "Bridge In Tech is an application inspired by the existing AnitaB.org Mentorship System, "
+ "It encourages organizations to collaborate with the mentors and mentees on mentoring programs. \n \n"
+ "The main repository of the Backend System can be found here: https://github.com/anitab-org/bridge-in-tech-backend \n \n"
+ "The Web client for the Mentorship System can be found here: https://github.com/anitab-org/bridge-in-tech-web \n \n"
+ "For more information about the project here's a link to our wiki guide: https://github.com/anitab-org/bridge-in-tech-backend/wiki"
# doc='/docs/'
)
api.namespaces.clear()

# Adding namespaces
from app.api.resources.users import users_ns as user_namespace

api.add_namespace(user_namespace, path="/")


Empty file added app/api/dao/__init__.py
Empty file.
37 changes: 37 additions & 0 deletions app/api/dao/user_extension.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from http import HTTPStatus
from typing import Dict
from sqlalchemy import func
from app.database.models.bit_schema.user_extension import UserExtensionModel
from app import messages


class UserExtensionDAO:

"""Data Access Object for Users_Extension functionalities"""

@staticmethod
def create_user_extension(data):
"""Creates a user_extension instance for a new registered user.
Arguments:
data: A list containing user's id, boolean value of whether or not
the user is representing an organization, as well as their timezone
Returns:
A dictionary containing "message" which indicates whether or not the user_exension was created successfully and "code" for the HTTP response code.
"""

user_id = data["user_id"]
is_organization_rep = data["is_organization_rep"]
timezone = data["timezone"]

user_extension = UserExtensionModel(user_id, is_organization_rep, timezone)

user_extension.save_to_db()

response = {
"message": f"{messages.USER_WAS_CREATED_SUCCESSFULLY}",
"code": f"{HTTPStatus.CREATED}",
}

return response
24 changes: 24 additions & 0 deletions app/api/jwt_extension.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from flask_jwt_extended import JWTManager
from http import HTTPStatus
from app import messages
from app.api.bit_extension import api

jwt = JWTManager()

# This is needed for the error handlers to work with flask-restplus
jwt._set_error_handler_callbacks(api)


@jwt.expired_token_loader
def my_expired_token_callback():
return messages.TOKEN_HAS_EXPIRED, HTTPStatus.UNAUTHORIZED


@jwt.invalid_token_loader
def my_invalid_token_callback(error_message):
return messages.TOKEN_IS_INVALID, HTTPStatus.UNAUTHORIZED


@jwt.unauthorized_loader
def my_unauthorized_request_callback(error_message):
return messages.AUTHORISATION_TOKEN_IS_MISSING, HTTPStatus.UNAUTHORIZED
3 changes: 3 additions & 0 deletions app/api/mail_extension.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from flask_mail import Mail

mail = Mail()
Empty file added app/api/models/__init__.py
Empty file.
24 changes: 24 additions & 0 deletions app/api/models/user_extension.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from flask_restx import fields, Model
from app.utils.bitschema_utils import Timezone

def add_models_to_namespace(api_namespace):
api_namespace.models[register_user_api_model.name] = register_user_api_model

register_user_api_model = Model(
"User registration model",
{
"name": fields.String(required=True, description="User name"),
"username": fields.String(required=True, description="User username"),
"password": fields.String(required=True, description="User password"),
"email": fields.String(required=True, description="User email"),
"terms_and_conditions_checked": fields.Boolean(
required=True, description="User check Terms and Conditions value"
),
"need_mentoring": fields.Boolean(
required=False, description="User need mentoring indication"
),
"available_to_mentor": fields.Boolean(
required=False, description="User availability to mentor indication"
),
},
)
58 changes: 58 additions & 0 deletions app/api/ms_api_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from flask import jsonify
import requests
# from requests.exceptions import HTTPError
from flask import json

from werkzeug.exceptions import HTTPException
import logging

# set base url

# for ms-api local server
BASE_MS_API_URL = "http://127.0.0.1:4000"

# for ms-api heroku server
# BASE_MS_API_URL = "https://bridge-in-tech-ms-test.herokuapp.com"

# @application.errorhandler(HTTPException)
# def handle_exception(e):
# """Return JSON instead of HTML for HTTP errors."""
# # start with the correct headers and status code from the error
# response = e.get_response()
# # replace the body with JSON
# response.data = json.dumps({
# "code": e.code,
# "name": e.name,
# "description": e.description,
# })
# response.content_type = "application/json"
# return response

# create instance
def post_request(request_url, data):
response = None,
try:

response_raw = requests.post(
request_url,
json = data,
headers = {"Accept": "application/json"}
)
response_raw.status_code = 201
response_raw.encoding = "utf-8"
response = response_raw.json()

except HTTPException as e:
response = e.get_response()
response.data = json.dumps({
"code": e.code,
"name": e.name,
"description": e.description,
})
response.content_type = "application/json"

print(f"{response}")
return response



Empty file added app/api/resources/__init__.py
Empty file.
47 changes: 47 additions & 0 deletions app/api/resources/users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from flask import request
from flask_restx import Resource, marshal, Namespace
from app.api.ms_api_utils import *
from app.api.dao.user_extension import UserExtensionDAO
from flask import json
from http import HTTPStatus
from app import messages
from app.api.models.user_extension import *

users_ns = Namespace("Users", description="Operations related to users")
add_models_to_namespace(users_ns)

DAO = UserExtensionDAO()

@users_ns.route("register")
class UserRegister(Resource):
@classmethod
@users_ns.doc("create_user")
@users_ns.response(HTTPStatus.CREATED, "%s" % messages.USER_WAS_CREATED_SUCCESSFULLY)
@users_ns.response(HTTPStatus.BAD_REQUEST, "%s" % messages.PASSWORD_INPUT_BY_USER_HAS_INVALID_LENGTH)
@users_ns.response(
HTTPStatus.CONFLICT,
"%s\n%s\n%s"
% (
messages.USER_USES_A_USERNAME_THAT_ALREADY_EXISTS,
messages.USER_USES_AN_EMAIL_ID_THAT_ALREADY_EXISTS,
),
)
@users_ns.response(HTTPStatus.INTERNAL_SERVER_ERROR, "%s" % messages.INTERNAL_SERVER_ERROR)
@users_ns.expect(register_user_api_model, validate=True)

def post(cls):
"""
Creates a new user.
The endpoint accepts user input related to Mentorship System API (name, username, password, email,
terms_and_conditions_checked(true/false), need_mentoring(true/false),
available_to_mentor(true/false)) and Bridge In Tech API (is_organization_rep and timezone).
A success message is displayed and verification email is sent to the user's email ID.
"""

data = request.json

# send POST /register request to MS API and return response
return post_request(f"{BASE_MS_API_URL}/register", data)


Empty file added app/api/validations/__init__.py
Empty file.
22 changes: 22 additions & 0 deletions app/api/validations/task_comment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from app import messages
from app.utils.validation_utils import validate_length, get_stripped_string

COMMENT_MAX_LENGTH = 400


def validate_task_comment_request_data(data):
if "comment" not in data:
return messages.COMMENT_FIELD_IS_MISSING

comment = data["comment"]

if not isinstance(comment, str):
return messages.COMMENT_NOT_IN_STRING_FORMAT

is_valid = validate_length(
len(get_stripped_string(data["comment"])), 0, COMMENT_MAX_LENGTH, "comment"
)
if not is_valid[0]:
return is_valid[1]

return {}
Loading

0 comments on commit c41978c

Please sign in to comment.