import Application from require "lapis" import assert_valid from require "lapis.validate" import APIKeys, Users, Tasks from require "models" import locate from require "locator" import api_request, abort from locate "helpers.api" -- import random from locate "calc" -- import escape_similar_to from locate "db" class API extends Application @path: "/v1" @name: "api_" @before_filter( api_request => -- TODO implement Authorization: api_key VALUE as acceptable method to send api_key abort "api_key not specified." unless @params.api_key -- this does not seem to be triggering!!! @api_key = APIKeys\find key: @params.api_key abort "Invalid api_key" unless @api_key @user = Users\find id: @api_key.user_id abort "Invalid api_key." unless @user -- NOTE this should also delete the api_key and error (this should never happen!) ) -- TODO intentionally cause an error to see if this is working as intended -- handle_error: (err, trace) => -- return status: 500, json: { errors: {err}, :trace } -- NOTE trace should be saved and NOT returned to the user [err_test: "/err"]: api_request => error "this is a testing error" [new: "/new"]: api_request => assert_valid @params, { {"content", exists: true, min_length: 1, "Task content not specified."} } task, err = Tasks\create { user_id: @user.id content: @params.content } abort 500, err unless task return json: { success: true, :task } [get: "/get"]: api_request => -- TODO abort 501, "Not implemented." [do: "/do"]: api_request => local task if @params.id assert_valid @params, { {"id", exists: true, min_length: 1, "Attempted to select by task id, but no id specified."} {"id", is_integer: true, "Task id is not an integer."} } task = Tasks\find id: @params.id, user_id: @user.id elseif @params.content assert_valid @params, { {"content", exists: true, min_length: 1, "Attempted to select by task content, but no content specified."} } task = Tasks\find content: @params.content, user_id: @user.id else abort "Task id or content not specified." abort 404, "Invalid task specified." unless task task, err = task\update done: true abort 500, err unless task return json: { success: true, :task } [undo: "/undo"]: api_request => local task if @params.id assert_valid @params, { {"id", exists: true, min_length: 1, "Attempted to select by task id, but no id specified."} {"id", is_integer: true, "Task id is not an integer."} } task = Tasks\find id: @params.id, user_id: @user.id elseif @params.content assert_valid @params, { {"content", exists: true, min_length: 1, "Attempted to select by task content, but no content specified."} } task = Tasks\find content: @params.content, user_id: @user.id else abort "Task id or content not specified." abort 404, "Invalid task specified." unless task task, err = task\update done: false abort 500, err unless task return json: { success: true, :task } [random: "/random"]: api_request => assert_valid @params, { {"count", exists: true, is_integer: true, optional: true, "Count is not an integer."} {"done", exists: true, one_of: {true, false}, optional: true, "Done is not a boolean."} } @params.count or= 1 @params.done = false if @params.done == nil abort 501, "Not implemented." -- TODO figure out how to return random selection -- possibly need to store how many items each user has and use a different strategy for users with low amounts vs high amounts -- key = get_key(@) -- -- local tasks -- if @params.done -- offset = random Tasks\count "user_id = ? AND done = ? ORDER BY id ASC", key.user_id, @params.done -- tasks = Tasks\select "WHERE user_id = ? AND done = ? ORDER BY id ASC OFFSET ? LIMIT 1", key.user_id, @params.done, offset -- else -- offset = random Tasks\count "user_id = ? ORDER BY id ASC", key.user_id -- tasks = Tasks\select "WHERE user_id = ? ORDER BY id ASC OFFSET ? LIMIT 1", key.user_id, offset -- -- if tasks and #tasks == 1 -- return json: { success: true, task: tasks[1] } -- else -- abort! [list: "/list"]: api_request => assert_valid @params, { {"count", exists: true, is_integer: true, optional: true, "Count is not an integer."} {"done", exists: true, one_of: {true, false}, optional: true, "Done is not a boolean."} {"page", exists: true, is_integer: true, optional: true, "Page is not an integer."} {"order", exists: true, one_of: {"asc", "desc"}, optional: true, "Invalid order. (Must be 'asc' or 'desc'.)"} } @params.count or= 50 @params.page or= 1 @params.order or= "asc" abort "Invalid page. (Must be a positive integer.)" if @params.page < 1 local Paginator if @params.done == nil Paginator = Tasks\paginated "WHERE user_id = ? ORDER BY created_at ?", @user.id, @params.order, per_page: @params.count else Paginator = Tasks\paginated "WHERE user_id = ? AND done = ? ORDER BY created_at ?", @user.id, @params.done, @params.order, per_page: @params.count num_pages = Paginator\num_pages! if num_pages < @params.page @params.page = num_pages tasks = Paginator\get_page(@params.page) -- returns page in case it returned a different page than you asked for! (in the case you ask for a page beyond the end) return json: { success: true, page: @params.page, :tasks } [new_key: "/key/new"]: api_request => api_key, err = APIKeys\create(@user) abort 500, err unless api_key return json: { success: true, :api_key } [delete_key: "/key/delete"]: api_request => -- TODO abort 501, "Not implemented." -- /new { content: "string" } -- /do { id: # } or content -- /undo { id: # } or content -- /get { id: # } or content -- /random { count: #, done: bool } (both args optional, defaults count 1, done false) -- /list { count: #, done: bool, page: #, order: asc/desc } (if done not specified, returns all, -- default count is 50, default page is 1, default order is latest first -- /key/new -- /key/delete { id: #, key: "str" }