Skip to content

Commit

Permalink
Add one on one API
Browse files Browse the repository at this point in the history
  • Loading branch information
recrsn committed Jan 12, 2021
1 parent a8b1526 commit 8128d5b
Show file tree
Hide file tree
Showing 10 changed files with 414 additions and 4 deletions.
126 changes: 125 additions & 1 deletion api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ components:
example:
message: 'Bad request'

PermissionDenied:
description: 'Permission denied'
content:
'application/json':
schema:
$ref: '#/components/schemas/Error'
example:
message: 'Permission denied'

schemas:
Error:
type: object
Expand All @@ -40,21 +49,73 @@ components:
name:
type: string
maxLength: 255
example: 'John Doe'
designation:
type: string
example: 'Manager'
team_name:
type: string
example: 'Sales'
email:
type: string
format: email
example: '[email protected]'
profile_pic:
type: string
format: url
example: 'https://cdn.example.com/images/dcd1674d3ddf42a59817202f.jpg'
username:
type: string
example: 'john.doe'

OneOnOneActionItem:
type: object
properties:
id:
type: integer
example: 995296
state:
type: boolean
example: false
content:
type: string
example: 'This is an action item'
created_by:
$ref: '#/components/schemas/User'

OneOnOne:
type: object
properties:
id:
type: integer
example: 261993
created_at:
type: string
format: date-time
example: '2021-01-11T22:40:08+05:30Z'
created_by:
$ref: '#/components/schemas/User'
user:
$ref: '#/components/schemas/User'

OneOnOneDetail:
allOf:
- $ref: '#/components/schemas/OneOnOne'
- type: object
properties:
action_items:
type: array
items:
- $ref: '#/components/schemas/OneOnOneActionItem'

CreateOneOnOne:
type: object
properties:
user:
type: string

paths:
'/v1/users/_autocomplete':
'/users/_autocomplete':
get:
summary: 'Autocomplete'
responses:
Expand All @@ -66,3 +127,66 @@ paths:
$ref: '#/components/schemas/User'
'400':
$ref: '#/components/responses/BadRequest'

'/one_on_ones':
get:
summary: 'Get One on Ones'
responses:
'200':
description: 'OK'
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/OneOnOne'
put:
summary: 'Create one on one'
requestBody:
content:
'application/json':
schema:
$ref: '#/components/schemas/CreateOneOnOne'
responses:
'201':
description: 'Created'
content:
'application/json':
schema:
$ref: '#/components/schemas/OneOnOneDetail'
'400':
$ref: '#/components/responses/BadRequest'
'403':
$ref: '#/components/responses/PermissionDenied'


'/one_on_ones/{id}':
parameters:
- in: path
name: id
schema:
type: integer
required: true
get:
summary: 'Get one on one by ID'
responses:
'200':
description: 'OK'
content:
'application/json':
schema:
$ref: '#/components/schemas/OneOnOneDetail'
'400':
$ref: '#/components/responses/BadRequest'
'403':
$ref: '#/components/responses/PermissionDenied'

delete:
summary: 'Delete one on one by ID'
responses:
'204':
description: 'OK'
'400':
$ref: '#/components/responses/BadRequest'
'403':
$ref: '#/components/responses/PermissionDenied'
1 change: 1 addition & 0 deletions snowflake/acl/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .one_on_one import *
11 changes: 11 additions & 0 deletions snowflake/acl/one_on_one.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from flask_login import current_user

from snowflake.models import OneOnOne, User


def can_view_one_on_one(one_on_one: OneOnOne, user: User = current_user):
return user.id == one_on_one.user_id or user.id == one_on_one.created_by_id


def can_delete_one_on_one(one_on_one: OneOnOne, user: User = current_user):
return can_view_one_on_one(one_on_one, user)
3 changes: 3 additions & 0 deletions snowflake/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ def load_user(user_id):


app.register_blueprint(index.blueprint)

app.register_blueprint(api.users.blueprint, url_prefix="/api/users")
app.register_blueprint(api.notifications.blueprint, url_prefix="/api/notifications")
app.register_blueprint(api.one_on_ones.blueprint, url_prefix="/api/one_on_ones")

app.register_blueprint(login.blueprint, url_prefix="/login")
app.register_blueprint(register.blueprint, url_prefix="/register")
app.register_blueprint(profile.blueprint, url_prefix="/profile")
Expand Down
3 changes: 2 additions & 1 deletion snowflake/controllers/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from . import users
from . import notifications
from . import one_on_ones
from . import users
192 changes: 192 additions & 0 deletions snowflake/controllers/api/one_on_ones.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
from datetime import datetime

from flask import Blueprint, request
from flask_login import login_required, current_user
from marshmallow import ValidationError

from .response import not_found, unauthorized, no_content, bad_request, validation_error
from ... import acl
from ...db import transaction, db
from ...models import OneOnOne, OneOnOneActionItem
from ...schemas.one_on_one import OneOnOneSchema, GetOneOnOneSchema, CreateOneOnOneSchema, OneOnOneActionItemSchema, \
CreateOrEditOneOnOneActionItemSchema

blueprint = Blueprint('api.one_on_ones', __name__)

one_on_one_schema = OneOnOneSchema()
one_on_one_action_item_schema = OneOnOneActionItemSchema()
get_one_on_one_schema = GetOneOnOneSchema()
create_one_on_one_schema = CreateOneOnOneSchema()
create_or_edit_one_on_one_action_item_schema = CreateOrEditOneOnOneActionItemSchema()


@login_required
@blueprint.route('', methods=['GET'])
def list_all():
return one_on_one_schema.jsonify(OneOnOne.get_by_user(current_user), many=True)


@login_required
@blueprint.route('', methods=['PUT'])
def create():
if not request.is_json():
return bad_request()

try:
one_on_one: OneOnOne = create_one_on_one_schema.load(request.json)
one_on_one.created_by = current_user
one_on_one.created_at = datetime.now()

with transaction():
OneOnOne.create(one_on_one)

return get_one_on_one_schema.jsonify(one_on_one)
except ValidationError as e:
return validation_error(e.messages)


@login_required
@blueprint.route('/<_id>', methods=['GET'])
def get(_id: int):
one_on_one = OneOnOne.get(_id)

if not one_on_one:
return not_found()

if not acl.can_view_one_on_one(one_on_one):
return unauthorized()

return get_one_on_one_schema.jsonify(one_on_one)


@login_required
@blueprint.route('/<one_on_one_id>/action_items', methods=['GET'])
def get_action_items(one_on_one_id: int):
one_on_one = OneOnOne.get(one_on_one_id)

if not one_on_one:
return not_found()

if not acl.can_view_one_on_one(one_on_one):
return unauthorized()

return one_on_one_action_item_schema.jsonify(one_on_one.action_items, many=True)


@login_required
@blueprint.route('/<one_on_one_id>/action_items/<action_item_id>', methods=['GET'])
def get_action_item(one_on_one_id: int, action_item_id: int):
one_on_one = OneOnOne.get(one_on_one_id)

if not one_on_one:
return not_found()

if not acl.can_view_one_on_one(one_on_one):
return unauthorized()

action_item = OneOnOneActionItem.get(action_item_id)

if not action_item:
return not_found()

if not action_item.one_on_one_id == one_on_one.id:
return not_found()

return one_on_one_action_item_schema.jsonify(action_item)


@login_required
@blueprint.route('/<one_on_one_id>/action_items/<action_item_id>', methods=['DELETE'])
def delete_action_item(one_on_one_id: int, action_item_id: int):
one_on_one = OneOnOne.get(one_on_one_id)

if not one_on_one:
return not_found()

if not acl.can_view_one_on_one(one_on_one):
return unauthorized()

action_item = OneOnOneActionItem.get(action_item_id)

if not action_item:
return not_found()

if not action_item.one_on_one_id == one_on_one.id:
return not_found()

with transaction():
db.session.remove(one_on_one)

return no_content()


@login_required
@blueprint.route('/<one_on_one_id>/action_items', methods=['PUT'])
def create_action_item(one_on_one_id: int):
if not request.is_json():
return bad_request()

one_on_one = OneOnOne.get(one_on_one_id)

if not one_on_one:
return not_found()

if not acl.can_view_one_on_one(one_on_one):
return unauthorized()

try:
action_item: OneOnOneActionItem = create_or_edit_one_on_one_action_item_schema.load(request.json)
action_item.one_on_one = one_on_one
action_item.created_by = current_user
action_item.created_at = datetime.now()

with transaction():
OneOnOneActionItem.create(action_item)

return one_on_one_action_item_schema.jsonify(action_item)
except ValidationError as e:
return validation_error(e.messages)


@login_required
@blueprint.route('/<one_on_one_id>/action_items/<action_item_id>', methods=['PATCH'])
def edit_action_item(one_on_one_id: int, action_item_id: int):
if not request.is_json():
return bad_request()

one_on_one = OneOnOne.get(one_on_one_id)

if not one_on_one:
return not_found()

if not acl.can_view_one_on_one(one_on_one):
return unauthorized()

action_item = OneOnOneActionItem.get(action_item_id)

try:
action_item: OneOnOneActionItem = create_or_edit_one_on_one_action_item_schema.load(request.json, action_item)

with transaction():
db.session.add(action_item)

return one_on_one_action_item_schema.jsonify(action_item)
except ValidationError as e:
return validation_error(e.messages)


@login_required
@blueprint.route('/<_id>', methods=['DELETE'])
def delete_one_on_one(_id: int):
one_on_one = OneOnOne.get(_id)

if not one_on_one:
return not_found()

if not acl.can_delete_one_on_one(one_on_one):
return unauthorized()

with transaction():
db.session.remove(one_on_one)

return no_content()
Loading

0 comments on commit 8128d5b

Please sign in to comment.