Skip to content

Commit

Permalink
Merge pull request #1 from msnodeve/develop
Browse files Browse the repository at this point in the history
[Merge] develop->master : first release
  • Loading branch information
msnodeve authored Jul 22, 2019
2 parents 05b1b5d + f9d3944 commit 8fcd697
Show file tree
Hide file tree
Showing 28 changed files with 1,357 additions and 1 deletion.
Binary file added .DS_Store
Binary file not shown.
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,9 @@
.venv

# vscode
.vscode
.vscode

# python
__pycache__

# database
15 changes: 15 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,23 @@ url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]
pytest-cov = "*"
pytest = "*"
pylint = "*"
autopep8 = "*"

[packages]
flask = "*"
flask-sqlalchemy = "*"
flask-migrate = "*"
pymysql = "*"
flask-restplus = "*"
flask-marshmallow = "*"
marshmallow-jsonapi = "*"
flask-script = "*"
flask-basicauth = "*"
pyjwt = "*"
flask-bcrypt = "*"

[requires]
python_version = "3.7"
531 changes: 531 additions & 0 deletions Pipfile.lock

Large diffs are not rendered by default.

Binary file added app/.DS_Store
Binary file not shown.
40 changes: 40 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""
app init
"""
from flask import Flask, render_template, jsonify
from flask_restplus import Resource, Api, fields, reqparse
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.sql import text
from flask_marshmallow import Marshmallow
from app.api.database import DB
from app.api import REST_API

SQLALCHEMY_DATABASE_URI = \
("mysql+pymysql://{USER}:{PASSWORD}@{ADDR}:{PORT}/{NAME}?charset=utf8")

# 설명할 API에 대한 것
MA = Marshmallow()

def create_app() -> (Flask):
""" create_app() 함수를 호출해 앱을 초기화 """

""" app config part """
# 나중에 config는 다 빼야 할 것 같다.
app = Flask(__name__)
app.app_context().push()
app.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URI.format(
USER="root",
PASSWORD="1234",
ADDR="127.0.0.1",
PORT=3306,
NAME="board"
)
app.config['SQLALCHEMY_ECHO'] = True
app.config['DEBUG'] = True
DB.init_app(app)
REST_API.init_app(app)
MA.init_app(app)

""" return part """
return app
9 changes: 9 additions & 0 deletions app/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from flask_restplus import Api
from app.users.views import API as users_api
from app.posts.views import API as posts_api
from app.api.auth_type import ACCESS_TOKEN, BASIC_AUTH

REST_API = Api(authorizations={**ACCESS_TOKEN, **BASIC_AUTH})

REST_API.add_namespace(users_api, '/user')
REST_API.add_namespace(posts_api, '/posts')
40 changes: 40 additions & 0 deletions app/api/auth_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from flask import request
from functools import wraps
import jwt

SECERET_KEY = "Hello"
ACCESS_TOKEN = {
'Access Token': {
'type': 'apiKey',
'in': 'header',
'name': 'Authorization'
}
}
BASIC_AUTH = {
'Basic Auth': {
'type': 'basic',
'in': 'header',
'name': 'Authorization'
},
}

def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
access_token = request.headers['Authorization']
if access_token is not None:
try:
payload = jwt.decode(access_token, SECERET_KEY, "HS256")
except jwt.InvalidTokenError:
payload = None

# if payload is None:
# return Response(status=401)

user_id = payload["user_id"]
# g.user = get_user_info(user_id) if user_id else None
else:
return Response(status=401)

return f(*args, **kwargs)
return decorated_function
66 changes: 66 additions & 0 deletions app/api/database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from flask import jsonify
from flask import make_response
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.exc import IntegrityError
from marshmallow import ValidationError
from app.constants import STATUS_CODE
from flask_restplus import reqparse

DB = SQLAlchemy()

class CRUD:
body = ''
status_code = STATUS_CODE.NOT_IMPLEMENTED
def add(self, resource, schema):
try:
DB.session.add(resource)
DB.session.commit()
query = resource.query.get(resource.id)
self.body = jsonify(schema.dump(resource).data)
self.status_code = STATUS_CODE.CREATED
except IntegrityError as error:
DB.session.rollback()
error_message = str(error)
self.body = jsonify({"error": error_message, "type":"IntegrityError"})
if "Duplicate entry" in error_message:
self.status_code = 404
else:
self.status_code = 400
finally:
response = (self.body, self.status_code.value)
response = make_response(response)

return response

def update(self, args, schema):
try:
for key, value in args.items():
setattr(self, key, value)
DB.session.commit()
self.body = jsonify(schema.dump(self).data)
self.status_code = STATUS_CODE.OK
except IntegrityError as error:
DB.session.rollback()
error_message = str(error)
self.body = jsonify({"error": error_message, "type":"IntegrityError"})
if "Duplicate entry" in error_message:
self.status_code = STATUS_CODE.CONFLICT
else:
self.status_code = STATUS_CODE.BAD_REQUEST
finally:
response = (self.body, self.status_code.value)
response = make_response(response)
return response

def delete(self, resource, schema):
DB.session.delete(resource)
DB.session.commit()
self.body = jsonify({"message":"success"})
self.status_code = STATUS_CODE.OK
response = (self.body, self.status_code.value)
response = make_response(response)
return response

def select(self, name, password):

return "test"
41 changes: 41 additions & 0 deletions app/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from enum import Enum

class STATUS_CODE(Enum):
OK = 200
CREATED = 201
NO_CONTENT = 204
BAD_REQUEST = 400
UNAUTHORIZED = 401
FORBIDDEN = 403
NOT_FOUND = 404
METHOD_NOT_ALLOWED = 405
CONFLICT = 409
INTERNAL_SERVER_ERROR = 500
NOT_IMPLEMENTED = 501
BAD_GATEWAY = 502

responses = {item.value: item.name for item in STATUS_CODE}

def support_codes(unsupport_codes):
unsupport_codes = [code.value for code in unsupport_codes]
return {code: name for code, name in responses.items() if code not in unsupport_codes}

GET = support_codes(unsupport_codes=[
STATUS_CODE.CREATED, STATUS_CODE.NO_CONTENT,
STATUS_CODE.CONFLICT,
STATUS_CODE.METHOD_NOT_ALLOWED,
STATUS_CODE.NOT_IMPLEMENTED])
POST = support_codes(unsupport_codes=[
STATUS_CODE.OK, STATUS_CODE.NO_CONTENT,
STATUS_CODE.METHOD_NOT_ALLOWED,
STATUS_CODE.NOT_IMPLEMENTED])
PATCH = support_codes(unsupport_codes=[
STATUS_CODE.CREATED,
STATUS_CODE.NO_CONTENT,
STATUS_CODE.METHOD_NOT_ALLOWED,
STATUS_CODE.NOT_IMPLEMENTED])
DELETE = support_codes(unsupport_codes=[
STATUS_CODE.CREATED,
STATUS_CODE.CONFLICT,
STATUS_CODE.METHOD_NOT_ALLOWED,
STATUS_CODE.NOT_IMPLEMENTED])
Empty file added app/posts/__init__.py
Empty file.
33 changes: 33 additions & 0 deletions app/posts/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from app.api.database import DB, CRUD
from marshmallow import Schema, fields
from flask_sqlalchemy import SQLAlchemy
from marshmallow import validate
from sqlalchemy.sql import text
from app.users.models import Users, UsersSchema

class Post(DB.Model):
__tablename__ = 'posts'
__table_args__ = {'mysql_collate': 'utf8_general_ci'}

id = DB.Column(DB.Integer, primary_key=True)
author_id = DB.Column(DB.Integer, DB.ForeignKey(Users.id))
name = DB.Column(DB.String(255), nullable=False)
title = DB.Column(DB.String(255), nullable=False)
body = DB.Column(DB.String(1024), nullable=False)
author = DB.relationship("Users", uselist=False)
created = DB.Column(DB.TIMESTAMP, server_default=text(
"CURRENT_TIMESTAMP"), nullable=False)

def __init__(self, name: str, title : str, body : str, author_id: int):
self.name = name
self.title = title
self.body = body
self.author_id = author_id

class PostSchema(Schema):
id = fields.Integer()
name = fields.Str()
title = fields.Str()
body = fields.Str()
author = fields.Nested(UsersSchema)
created = fields.Str()
86 changes: 86 additions & 0 deletions app/posts/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from http import HTTPStatus
from flask import jsonify
from flask import make_response
from app.users.models import Users, UsersSchema
from sqlalchemy.exc import SQLAlchemyError
from flask_restplus import Api, Namespace, fields, reqparse, Resource
from app.constants import STATUS_CODE
from app.constants import GET, POST, PATCH, DELETE
from app.posts.models import Post, PostSchema
from app.api.database import DB
from app.api.auth_type import login_required
from app.api.auth_type import BASIC_AUTH, ACCESS_TOKEN, SECERET_KEY

API = Namespace('Posts', description="Post's REST API")

POSTS_SCHEMA = PostSchema()

POST_FIELDS = API.model('Post', {
'name': fields.String,
'title': fields.String,
'body': fields.String,
'author_id': fields.Integer,
})

@API.route('')
class Posts(Resource):
parser = reqparse.RequestParser()
parser.add_argument('name', required=True, type=str,
help="post's name", location='json')
parser.add_argument('title', required=True, type=str,
help="post's title", location='json')
parser.add_argument('body', required=True, type=str,
help="post's body", location='json')
parser.add_argument('author_id', required=True, type=int,
help="post's author", location='json')

@API.doc(responses=GET, security=ACCESS_TOKEN)
@login_required
def get(self):
try:
posts_query = Post.query.all()
body = jsonify(POSTS_SCHEMA.dump(posts_query, many=True).data)
if posts_query:
code = HTTPStatus.OK
else:
code = HTTPStatus.NOT_FOUND
except SQLAlchemyError as err:
message = str(err)
body = jsonify({"message": message})
code = HTTPStatus.INTERNAL_SERVER_ERROR
return make_response(body, code.value)

@API.expect(POST_FIELDS)
def post(self):
args_ = self.parser.parse_args()
post = Post(name=args_['name'], title=args_['title'], body=args_[
'body'], author_id=args_['author_id'])
try:
DB.session.add(post)
DB.session.commit()
body = jsonify({"posts": POSTS_SCHEMA.dump(post).data})
code = HTTPStatus.OK
except SQLAlchemyError as err:
DB.session.rollback()
message = str(err)
body = jsonify({"message": message})
code = HTTPStatus.INTERNAL_SERVER_ERROR
return make_response(body, code.value)


@API.route('/<int:seqno>')
class PostItem(Resource):
def get(self, seqno):
try:
post_item = DB.session.query(Post).outerjoin(
Users, Users.id == Post.author_id).filter(Post.id == seqno).first()
body = jsonify({"post": POSTS_SCHEMA.dump(post_item).data})
if post_item:
code = HTTPStatus.OK
else:
code = HTTPStatus.NOT_FOUND
except SQLAlchemyError as err:
message = str(err)
body = jsonify({"message": message})
code = HTTPStatus.INTERNAL_SERVER_ERROR
return make_response(body, code.value)
Empty file added app/tests/__init__.py
Empty file.
10 changes: 10 additions & 0 deletions app/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import pytest
from app import create_app # flask application factory

@pytest.fixture(scope='session')
def flask_app():
app = create_app()
app_context = app.app_context()
app_context.push()
yield app
app_context.pop()
Loading

0 comments on commit 8fcd697

Please sign in to comment.