From 4bb96b28dee16e78d68f16f5bf158f1e879a0523 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Sep 2011 22:11:36 +0200 Subject: API first draft: utils. (related to #27) Introduces the "rest" module, with reusable utils for flask applications (will be packaged as a flask extension later on). --- budget/api.py | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 budget/api.py (limited to 'budget/api.py') diff --git a/budget/api.py b/budget/api.py new file mode 100644 index 0000000..70864a6 --- /dev/null +++ b/budget/api.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +from flask import * +import werkzeug + +from models import db, Project, Person, Bill +from utils import for_all_methods + +from rest import RESTResource, DefaultHandler, need_auth # FIXME make it an ext + + +api = Blueprint("api", __name__, url_prefix="/api") + +def check_project(*args, **kwargs): + """Check the request for basic authentication for a given project. + + Return the project if the authorization is good, False otherwise + """ + auth = request.authorization + + # project_id should be contained in kwargs and equal to the username + if auth and "project_id" in kwargs and \ + auth.username == kwargs["project_id"]: + project = Project.query.get(auth.username) + if project.password == auth.password: + return project + return False + + +class ProjectHandler(DefaultHandler): + + def get(self, *args, **kwargs): + return "get" + + def delete(self, *args, **kwargs): + return "delete" + +project_resource = RESTResource( + name="project", + route="/project", + app=api, + actions=["add", "update", "delete", "get"], + authentifier=check_project, + handler=ProjectHandler()) + +# projects: add, delete, edit, get +# GET /project/ → get +# PUT /project/ → add & edit +# DELETE /project/ → delete + +# project members: list, add, delete +# GET /project//members → list +# POST /project//members/ → add +# PUT /project//members/ → edit +# DELETE /project//members/ → delete + +# project bills: list, add, delete, edit, get +# GET /project//bills → list +# GET /project//bills/ → get +# DELETE /project//bills/ → delete +# POST /project//bills/ → add + + +# GET, PUT, DELETE: / : Get, update and delete +# GET, POST: / Add & List -- cgit v1.1 From ef3d761fc70c1c2bf0d45dde6d2703c73715bf6c Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Sep 2011 23:00:32 +0200 Subject: Add Handlers for members and bills. --- budget/api.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 5 deletions(-) (limited to 'budget/api.py') diff --git a/budget/api.py b/budget/api.py index 70864a6..e768bd9 100644 --- a/budget/api.py +++ b/budget/api.py @@ -5,7 +5,7 @@ import werkzeug from models import db, Project, Person, Bill from utils import for_all_methods -from rest import RESTResource, DefaultHandler, need_auth # FIXME make it an ext +from rest import RESTResource, need_auth # FIXME make it an ext api = Blueprint("api", __name__, url_prefix="/api") @@ -26,22 +26,83 @@ def check_project(*args, **kwargs): return False -class ProjectHandler(DefaultHandler): +class ProjectHandler(object): - def get(self, *args, **kwargs): + def add(self): + pass + + @need_auth(check_project, "project") + def get(self, project): return "get" - def delete(self, *args, **kwargs): + @need_auth(check_project, "project") + def delete(self, project): return "delete" + @need_auth(check_project, "project") + def update(self, project): + return "update" + + +class MemberHandler(object): + + def get(self, project, member_id): + pass + + def list(self, project): + pass + + def add(self, project): + pass + + def update(self, project, member_id): + pass + + def delete(self, project, member_id): + pass + + +class BillHandler(object): + + def get(self, project, member_id): + pass + + def list(self, project): + pass + + def add(self, project): + pass + + def update(self, project, member_id): + pass + + def delete(self, project, member_id): + pass + + project_resource = RESTResource( name="project", route="/project", app=api, actions=["add", "update", "delete", "get"], - authentifier=check_project, handler=ProjectHandler()) +member_resource = RESTResource( + name="member", + inject_name="project", + route="/project//members", + app=api, + handler=MemberHandler(), + authentifier=check_project) + +bill_resource = RESTResource( + name="bill", + inject_name="project", + route="/project//bills", + app=api, + handler=BillHandler(), + authentifier=check_project) + # projects: add, delete, edit, get # GET /project/ → get # PUT /project/ → add & edit -- cgit v1.1 From d2e2260e522cf4b7de24dbb2adbb3bfb12ee01f3 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Tue, 13 Sep 2011 11:27:36 +0200 Subject: Add a serialization mechanism --- budget/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'budget/api.py') diff --git a/budget/api.py b/budget/api.py index e768bd9..c307fdf 100644 --- a/budget/api.py +++ b/budget/api.py @@ -50,7 +50,7 @@ class MemberHandler(object): pass def list(self, project): - pass + return project.members def add(self, project): pass -- cgit v1.1 From e13ceaf351d4b54dd2bc651d9f4385a8188b7418 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Tue, 13 Sep 2011 18:15:07 +0200 Subject: REST API is now able to list stuff \o/ --- budget/api.py | 53 +++++++++++++++++++++-------------------------------- 1 file changed, 21 insertions(+), 32 deletions(-) (limited to 'budget/api.py') diff --git a/budget/api.py b/budget/api.py index c307fdf..c50d668 100644 --- a/budget/api.py +++ b/budget/api.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- from flask import * -import werkzeug from models import db, Project, Person, Bill from utils import for_all_methods -from rest import RESTResource, need_auth # FIXME make it an ext +from rest import RESTResource, need_auth# FIXME make it an ext +from werkzeug import Response api = Blueprint("api", __name__, url_prefix="/api") @@ -33,7 +33,7 @@ class ProjectHandler(object): @need_auth(check_project, "project") def get(self, project): - return "get" + return project @need_auth(check_project, "project") def delete(self, project): @@ -47,7 +47,10 @@ class ProjectHandler(object): class MemberHandler(object): def get(self, project, member_id): - pass + member = Person.query.get(member_id) + if not member or member.project != project: + return Response('Not Found', status=404) + return member def list(self, project): return project.members @@ -59,25 +62,32 @@ class MemberHandler(object): pass def delete(self, project, member_id): - pass + if project.remove_member(member_id): + return Response('OK', status=200) class BillHandler(object): - def get(self, project, member_id): - pass + def get(self, project, bill_id): + bill = Bill.query.get(project, bill_id) + if not bill: + return Response('Not Found', status=404) + return bill def list(self, project): - pass + return project.get_bills().all() def add(self, project): pass - def update(self, project, member_id): + def update(self, project, bill_id): pass - def delete(self, project, member_id): - pass + def delete(self, project, bill_id): + bill = Bill.query.delete(project, bill_id) + if not bill: + return Response('Not Found', status=404) + return bill project_resource = RESTResource( @@ -102,24 +112,3 @@ bill_resource = RESTResource( app=api, handler=BillHandler(), authentifier=check_project) - -# projects: add, delete, edit, get -# GET /project/ → get -# PUT /project/ → add & edit -# DELETE /project/ → delete - -# project members: list, add, delete -# GET /project//members → list -# POST /project//members/ → add -# PUT /project//members/ → edit -# DELETE /project//members/ → delete - -# project bills: list, add, delete, edit, get -# GET /project//bills → list -# GET /project//bills/ → get -# DELETE /project//bills/ → delete -# POST /project//bills/ → add - - -# GET, PUT, DELETE: / : Get, update and delete -# GET, POST: / Add & List -- cgit v1.1 From 8528526f0b6dc8828247ef03f11e4894580f8dd5 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Tue, 13 Sep 2011 19:24:48 +0200 Subject: API: set the mimetype on the response --- budget/api.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'budget/api.py') diff --git a/budget/api.py b/budget/api.py index c50d668..ddaf65c 100644 --- a/budget/api.py +++ b/budget/api.py @@ -64,6 +64,8 @@ class MemberHandler(object): def delete(self, project, member_id): if project.remove_member(member_id): return Response('OK', status=200) + else: + return Response('Not Found', status=404) class BillHandler(object): -- cgit v1.1 From b0d41291afade8aec86502d07d1d29d000ff1bca Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Tue, 13 Sep 2011 22:58:53 +0200 Subject: API: Create and Update support --- budget/api.py | 65 ++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 14 deletions(-) (limited to 'budget/api.py') diff --git a/budget/api.py b/budget/api.py index ddaf65c..3df8ab2 100644 --- a/budget/api.py +++ b/budget/api.py @@ -2,6 +2,7 @@ from flask import * from models import db, Project, Person, Bill +from forms import ProjectForm from utils import for_all_methods from rest import RESTResource, need_auth# FIXME make it an ext @@ -21,7 +22,7 @@ def check_project(*args, **kwargs): if auth and "project_id" in kwargs and \ auth.username == kwargs["project_id"]: project = Project.query.get(auth.username) - if project.password == auth.password: + if project and project.password == auth.password: return project return False @@ -29,7 +30,13 @@ def check_project(*args, **kwargs): class ProjectHandler(object): def add(self): - pass + form = ProjectForm(csrf_enabled=False) + if form.validate(): + project = form.save(Project()) + db.session.add(project) + db.session.commit() + return 201, project.id + return 400, form.errors @need_auth(check_project, "project") def get(self, project): @@ -37,11 +44,18 @@ class ProjectHandler(object): @need_auth(check_project, "project") def delete(self, project): - return "delete" + db.session.delete(project) + db.session.commit() + return 200, "DELETED" @need_auth(check_project, "project") def update(self, project): - return "update" + form = ProjectForm(csrf_enabled=False) + if form.validate(): + form.save(project) + db.session.commit() + return 200, "UPDATED" + return 400, form.errors class MemberHandler(object): @@ -49,23 +63,34 @@ class MemberHandler(object): def get(self, project, member_id): member = Person.query.get(member_id) if not member or member.project != project: - return Response('Not Found', status=404) + return 404, "Not Found" return member def list(self, project): return project.members def add(self, project): - pass + form = MemberForm(csrf_enabled=False) + if form.validate(): + member = Person() + form.save(project, member) + db.session.commit() + return 200, member.id + return 400, form.errors def update(self, project, member_id): - pass + form = MemberForm(csrf_enabled=False) + if form.validate(): + member = Person.query.get(member_id, project) + form.save(project, member) + db.session.commit() + return 200, member + return 400, form.errors def delete(self, project, member_id): if project.remove_member(member_id): - return Response('OK', status=200) - else: - return Response('Not Found', status=404) + return 200, "OK" + return 404, "Not Found" class BillHandler(object): @@ -73,22 +98,34 @@ class BillHandler(object): def get(self, project, bill_id): bill = Bill.query.get(project, bill_id) if not bill: - return Response('Not Found', status=404) + return 404, "Not Found" return bill def list(self, project): return project.get_bills().all() def add(self, project): - pass + form = BillForm(csrf_enabled=False) + if form.validate(): + bill = Bill() + form.save(bill) + db.session.add(bill) + db.session.commit() + return 200, bill.id + return 400, form.errors def update(self, project, bill_id): - pass + form = BillForm(csrf_enabled=False) + if form.validate(): + form.save(bill) + db.session.commit() + return 200, bill.id + return 400, form.errors def delete(self, project, bill_id): bill = Bill.query.delete(project, bill_id) if not bill: - return Response('Not Found', status=404) + return 404, "Not Found" return bill -- cgit v1.1