From 44ace72a194da3148ed74ed28c42a2bccbecf688 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Alexis=20Laferri=C3=A8re?= Date: Fri, 1 Apr 2016 11:54:16 -0400 Subject: [PATCH] contrib/benitlux: use nitrestful to expose social network services to clients MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Alexis Laferrière --- contrib/benitlux/.gitignore | 2 +- contrib/benitlux/Makefile | 16 +- contrib/benitlux/src/benitlux_controller.nit | 284 +++++++++++++++++++++++++- contrib/benitlux/src/benitlux_web.nit | 1 + 4 files changed, 290 insertions(+), 13 deletions(-) diff --git a/contrib/benitlux/.gitignore b/contrib/benitlux/.gitignore index 2ccbe7b..562fb6d 100644 --- a/contrib/benitlux/.gitignore +++ b/contrib/benitlux/.gitignore @@ -1,4 +1,4 @@ -src/benitlux_serial.nit +src/benitlux_restful.nit *.db *.email benitlux_corrections.txt diff --git a/contrib/benitlux/Makefile b/contrib/benitlux/Makefile index be1543b..c14d648 100644 --- a/contrib/benitlux/Makefile +++ b/contrib/benitlux/Makefile @@ -1,19 +1,19 @@ -all: server +SERVER ?= localhost:8080 -pre-build: src/benitlux_serial.nit +all: server +server: bin/benitlux_daily bin/benitlux_web bin/benitlux_daily: $(shell ../../bin/nitls -M src/benitlux_daily.nit) mkdir -p bin/ ../../bin/nitc -o $@ src/benitlux_daily.nit -bin/benitlux_web: $(shell ../../bin/nitls -M src/benitlux_web.nit) +bin/benitlux_web: $(shell ../../bin/nitls -M src/benitlux_web.nit) src/benitlux_restful.nit mkdir -p bin/ - ../../bin/nitc -o $@ src/benitlux_web.nit - -server: bin/benitlux_daily bin/benitlux_web + ../../bin/nitc -o $@ src/benitlux_web.nit -D iface=$(SERVER) -src/benitlux_serial.nit: - ../../bin/nitserial -o $@ src/benitlux_web.nit +pre-build: src/benitlux_restful.nit +src/benitlux_restful.nit: $(shell ../../bin/nitls -M src/benitlux_controller.nit) + ../../bin/nitrestful -o $@ src/benitlux_controller.nit # --- # Report diff --git a/contrib/benitlux/src/benitlux_controller.nit b/contrib/benitlux/src/benitlux_controller.nit index 806b42e..b6562bd 100644 --- a/contrib/benitlux/src/benitlux_controller.nit +++ b/contrib/benitlux/src/benitlux_controller.nit @@ -18,12 +18,15 @@ module benitlux_controller import nitcorn +import nitcorn::restful private import json::serialization import benitlux_model import benitlux_db import benitlux_view +import benitlux_social +# Server action for REST or Web, for a given location abstract class BenitluxAction super Action @@ -83,12 +86,248 @@ class BenitluxSubscriptionAction end end -# RESTful interface to compare beer offer between given dates -# -# Expects request in the format of `since/2014-07-24`, will replay with a -# `BeerEvents` serialized to Json with the necessary meta-data to be deserialized. +# RESTful interface for the client app class BenitluxRESTAction super BenitluxAction + super RestfulAction + + # Sign up a new user + # + # signup?name=a&pass=b&email=c -> LoginResult | BenitluxError + fun signup(name, pass, email: String): HttpResponse + is restful do + if not name.name_is_ok then + var error = new BenitluxError("Invalid username") + return new HttpResponse.ok(error) + end + + if not pass.pass_is_ok then + var error = new BenitluxError("Invalid password") + return new HttpResponse.ok(error) + end + + # Query DB + var db = new DB.open(db_path) + var error_message = db.signup(name, pass, email) + + var object: nullable Serializable + if error_message == null then + object = db.login(name, pass) + else + object = new BenitluxError(error_message) + end + db.close + + if object == null then + # There was an error in the call to login + return new HttpResponse.server_error + end + + # It went ok, may or may not be signed up + return new HttpResponse.ok(object) + end + + # Attempt to login + # + # login?name=a&pass=b -> LoginResult | BenitluxError + fun login(name, pass: String): HttpResponse + is restful do + var db = new DB.open(db_path) + var log: nullable Serializable = db.login(name, pass) + db.close + + if log == null then log = new BenitluxError("Login Failed", "Invalid username and password combination.") + + return new HttpResponse.ok(log) + end + + # Search a user + # + # search?token=b&query=a&offset=0 -> Array[UserAndFollowing] | BenitluxError + fun search(token: nullable String, query: String): HttpResponse + is restful do + var db = new DB.open(db_path) + var user_id = db.token_to_id(token) + var users = db.search_users(query, user_id) + db.close + + if users == null then return new HttpResponse.server_error + + return new HttpResponse.ok(users) + end + + # List available beers + # + # list?token=a[&offset=0&count=1] -> Array[BeerAndRatings] | BenitluxError + fun list(token: nullable String): HttpResponse + is restful do + + # Query DB + var db = new DB.open(db_path) + var user_id = db.token_to_id(token) + var list = db.list_beers_and_rating(user_id) + db.close + + if list == null then return new HttpResponse.server_error + + return new HttpResponse.ok(list) + end + + # Post a review of `beer` + # + # review?token=a&beer=b&rating=0 -> true | BenitluxError + fun review(token: String, rating, beer: Int): HttpResponse + is restful do + # Query DB + var db = new DB.open(db_path) + var user_id = db.token_to_id(token) + + if user_id == null then + db.close + return new HttpResponse.invalid_token + end + + db.post_review(user_id, beer, rating, "") + db.close + + return new HttpResponse.ok(true) + end + + # Set whether user of `token` follows `user_to`, by default set as follow + # + # follow?token=a&user_to=0 -> true | BenitluxError + fun follow(token: String, user_to: Int, follow: nullable Bool): HttpResponse + is restful do + + # Query DB + var db = new DB.open(db_path) + var user = db.token_to_id(token) + + if user == null then + db.close + return new HttpResponse.invalid_token + end + + if follow or else true then + db.add_followed(user, user_to) + else db.remove_followed(user, user_to) + + db.close + + return new HttpResponse.ok(true) + end + + # List followers of the user of `token` + # + # followers?token=a -> Array[UserAndFollowing] | BenitluxError | BenitluxError + fun followers(token: String): HttpResponse + is restful do + # Query DB + var db = new DB.open(db_path) + var user = db.token_to_id(token) + if user == null then + db.close + return new HttpResponse.invalid_token + end + var users = db.followers(user) + db.close + + if users == null then return new HttpResponse.server_error + + return new HttpResponse.ok(users) + end + + # List users followed by the user of `token` + # + # followed?token=a -> Array[UserAndFollowing] | BenitluxError + fun followed(token: String): HttpResponse + is restful do + # Query DB + var db = new DB.open(db_path) + var user = db.token_to_id(token) + if user == null then + db.close + return new HttpResponse.invalid_token + end + var users = db.followed(user) + db.close + + if users == null then return new HttpResponse.server_error + + return new HttpResponse.ok(users) + end + + # List friends of the user of `token` + # + # friends?token=a -> Array[UserAndFollowing] | BenitluxError + fun friends(token: String, n: nullable Int): HttpResponse + is restful do + # Query DB + var db = new DB.open(db_path) + var user = db.token_to_id(token) + var users = db.friends(user, n) + db.close + + if users == null then return new HttpResponse.server_error + + return new HttpResponse.ok(users) + end + + # Check user in or out + # + # checkin?token=a -> true | BenitluxError + fun checkin(token: String, is_in: nullable Bool): HttpResponse + is restful do + var db = new DB.open(db_path) + var id = db.token_to_id(token) + if id == null then + db.close + return new HttpResponse.invalid_token + end + + # Register in DB + db.checkin(id, is_in or else true) + + # Update followed_followers + var common_followers = db.followed_followers(id) + db.close + + return new HttpResponse.ok(true) + end + + # List users currently checked in among friends of the user of `token` + # + # checkedin?token=a -> Array[UserAndFollowing] + fun checkedin(token: String): HttpResponse + is restful do + var db = new DB.open(db_path) + var user_id = db.token_to_id(token) + if user_id == null then + db.close + return new HttpResponse.invalid_token + end + var report = db.checkedin_followed_followers(user_id) + db.close + + if report == null then return new HttpResponse.server_error + return new HttpResponse.ok(report) + end + + # List beer changes since `date` with information in relation to the user of `token` + # + # since?token=a&date=date -> BeerEvents + fun since(token, date: nullable String): HttpResponse + is restful do + # Query DB + var db = new DB.open(db_path) + var user_id = db.token_to_id(token) + var list = db.list_beers_and_rating(user_id, date) + db.close + + if list == null then return new HttpResponse.server_error + + return new HttpResponse.ok(list) + end redef fun answer(request, turi) do @@ -124,6 +363,9 @@ class BenitluxRESTAction end end +# --- +# Misc services + redef class Text # Rewrite the date represented by `self` in the format expected by SQLite private fun std_date: String @@ -141,3 +383,37 @@ redef class Text return "{y}-{m}-{d}" end end + +redef class HttpResponse + + # Respond with `data` in Json and a code 200 + init ok(data: Serializable) + do + init 200 + body = data.to_json_string + end + + # Respond with a `BenitluxError` in JSON and a code 403 + init invalid_token + do + init 403 + var error = new BenitluxTokenError("Forbidden", "Invalid or outdated token.") + body = error.to_json_string + end + + # Respond with a `BenitluxError` in JSON and a code 400 + init bad_request + do + init 400 + var error = new BenitluxError("Bad Request", "Application error, or it needs to be updated.") + body = error.to_json_string + end + + # Respond with a `BenitluxError` in JSON and a code 500 + init server_error + do + init 500 + var error = new BenitluxError("Internal Server Error", "Server error, try again later.") + body = error.to_json_string + end +end diff --git a/contrib/benitlux/src/benitlux_web.nit b/contrib/benitlux/src/benitlux_web.nit index 9739241..302b5de 100644 --- a/contrib/benitlux/src/benitlux_web.nit +++ b/contrib/benitlux/src/benitlux_web.nit @@ -20,6 +20,7 @@ module benitlux_web import benitlux_model import benitlux_view import benitlux_controller +import benitlux_restful # Listening interface fun iface: String do return "localhost:8080" -- 1.7.9.5