import Widget from require "lapis.html" import domain from require("lapis.config").get! if domain == "localhost" domain = "http://localhost" else domain = "https://#{domain}" example_key = "JDJiJDEyJFRPaG0wOW16VXhoUTd3dElB" class Docs_1 extends Widget content: => task_list = -> ul style: "font-family: monospace;", -> li -> a href: "#task-operations", "/new/:content" text " { content: string }" li -> a href: "#task-operations", "/get/:selector" text " { id: integer, content: string }" li -> a href: "#task-operations", "/do/:selector" text " { id: integer, content: string }" li -> a href: "#task-operations", "/undo/:selector" text " { id: integer, content: string }" li -> a href: "#task-operations", "/edit/:selector" text " { id: integer, content: string, new_content: string }" li -> a href: "#task-operations", "/delete/:selector" text " { id: integer, content: string }" li -> a href: "#list", "/list" text " { count: integer, done: boolean, page: integer, order: 'asc'/'desc' }" li -> a href: "#random", "/random" text " { count: integer, done: boolean }" style -> raw " h3 { font-family: monospace; } .top { font-size: 1.33rem; } blockquote > code { white-space: normal; } " a name: "top" ol style: "font-family: monospace;", -> li -> a href: "#endpoints", "Endpoints" li -> a href: "#auth", "Authorization" li -> a href: "#tasks", "Tasks" task_list! li -> a href: "#keys", "API Keys" ul -> li -> a href: "#new-key", "/key/new" li -> a href: "#delete-key", "/key/delete/:key" text " { key: string }" li -> a href: "#errors", "Errors" li -> a href: "#rate-limit", "Rate Limiting" li -> a href: "#changes", "Changes" li -> a href: "#next", "Coming Next" li -> a href: "#notes", "Notes/Tips" a name: "endpoints" h2 "Endpoints" p -> text "All endpoints are under " code "#{domain}/v1" text ", and accept parameters in the URL hierarchy, from POSTed JSON, in URI query string params, and for API keys, in an Authorization header. Most parameters are optional, with the only required parameters specified as part of the URL hierarchy." p -> text "All endpoints return HTTP status 200 when successful, as well as a JSON object (" code '{ "success": true }' text ") with a success boolean and extra data depending on the endpoint. When errors are encountered, a non-200 HTTP status code is sent, along with a JSON object (" code '{ "success": false, "errors": [ /*list of strings omitted*/ ] }' text ") with an array of errors (usually containing a single error)." button class: "collapsible", "Examples" div class: "content", -> p -> code "#{domain}/v1/get/12?api_key=#{example_key}" br! text "(assuming a task with ID " code 12 text " belongs to you, and the " code "api_key" text " is valid):" br! pre -> code title: "(This example is a real task that I added while developing this API. :P)", "{\"success\":true,\"task\":{\"created_at\":\"2018-04-24 14:15:04\",\"id\":12,\"content\":\"Make index return a logged_out view instead of redirecting to login\",\"done\":true,\"user_id\":1,\"updated_at\":\"2018-04-25 04:28:36\"}}" a class: ".top", href: "#top", "back to top" a name: "auth" h2 "Authorization" p "Authorization is done by sending an API key along with other parameters. It can be sent as an Authorization header, in a URI query string (not recommended), or as part of a POSTed JSON object." p -> text "Grab an API key from " a href: @url_for("index"), "the web interface" text " to get started. You can also delete existing API keys there." button class: "collapsible", "Examples" div class: "content", -> p -> text "As a header:" br! code "Authorization: #{example_key}" p -> text "As part of a JSON object: (POSTing to " code "#{domain}/v1/get" text " in this example)" br! code "{ \"id\": 12, \"api_key\": \"#{example_key}\" }" a class: ".top", href: "#top", "back to top" a name: "tasks" h2 "Tasks" p -> text "In the endpoints listed below, " code ":content" text " represents an optional URI-encoded string (for " code "/new/:content" text " only), and " code ":selector" text " represents an integer for selecting via ID, or a URI-encoded string for selecting via content (generally not recommended). All endpoints except " code "/list" text " and " code "/random" text " accept these in the URL, as query parameters, or in a POSTed JSON object, and operate on a single task." p -> code "/list" text " and " code "/random" text " are a little more complex, and are explained further below." task_list! a class: ".top", href: "#top", "back to top" a name: "task-operations" h2 "/new, /get, /do, /undo, /edit, /delete" p "As stated above, all of these operations accept the same data from the same formats, and return data in the same format, except:" ul -> li -> code "/new" text " only accepts " code "content" text " for input (you can't specify an ID for something that doesn't exist..)" li -> code "/edit" text " additionally accepts a " code "done" text " boolean, and/or " code "new_content" text " specifying how to modify a task." li -> code "/delete" text " only returns a success boolean (and errors array if there are errors)." p -> text "All tasks are returned like so:" br! pre -> code '{ "success": true, "task": { "id": 4, "user_id": 2, "content": "Get a new API key.", "done": false, "created_at": "2018-04-25 04:27:47", "updated_at": "2018-04-25 04:27:47" } }' button class: "collapsible", "Examples" div class: "content", -> p -> text "Authorization Header and URL parameter only:" br! text "Header: " code "Authorization: #{example_key}" br! text "URL: " code "#{domain}/v1/do/193" br! text "(Assuming everything is correct, would return a task, now marked done (regardless of previous value).)" p -> text "Using only query parameters (not a good idea!):" br! code "#{domain}/v1/get?api_key=#{example_key}&id=52" br! text "(Would return whatever task 52 is.. if it's yours. :P)" p -> text "Using JSON only:" br! code "{ \"api_key\": \"#{example_key}\", \"content\": \"I gotta do something..something useful!\" }" br! text "POSTed to: " code "#{domain}/v1/new" br! text "(And of course, would return the new task.)" a class: ".top", href: "#top", "back to top" a name: "list" h3 "/list" p -> text "Returns an array of task objects (see " a href: "#new", "/new" text " for an example task object). All arguments are optional:" element "table", -> thead -> tr -> th "Argument" th "Type" th "Default" tbody -> tr -> td -> code "count" td "integer" td 50 tr -> td -> code "done" td "boolean" td "returns ALL tasks if not set" tr -> td -> code "page" td "integer" td 1 tr -> td -> code "order" td "'asc'/'desc'" td "'asc' (oldest first)" p -> text "If you request a page < 1, will return first page. Likewise, requesting a page beyond the number of pages available will return the last page. " code "page" text " is specified in each response, so you will know if you have received a different page than requested. Additionally, " code "pages" text " is defined in all responses, telling you how many pages there are." p -> text "Here is an example response (with tasks omitted):" br! code '{ "success": true, page: 5, pages: 10, tasks: [ /* task list omitted */ ] }' a class: ".top", href: "#top", "back to top" a name: "random" h3 "/random" p "This is the only endpoint not currently implemented. This documentation will be updated once it is finished." -- TODO /random { count: #, done: bool } (both args optional, defaults count 1, done false) a class: ".top", href: "#top", "back to top" a name: "keys" h2 "API Keys" p "The API can be used to request new API keys and delete API keys, but cannot be used to look up API keys, or perform any other actions with API keys." a class: ".top", href: "#top", "back to top" a name: "new-key" h3 "/key/new" p -> text "Send an empty request. Receive something like the following:" br! pre -> code "{ \"success\": true, \"api_key\": { \"user_id\": 1, \"key\": \"#{example_key}\", \"created_at\": \"2018-04-25 06:05:53\", \"updated_at\": \"2018-04-25 06:05:53\" } }" a class: ".top", href: "#top", "back to top" a name: "delete-key" h3 "/key/delete/:key" p -> text "Send a POST request with " code "id" text " or " code "key" text " defined to delete an API key. Returns just the " code '{ "success": true }' text " JSON if successful." br! b "Note" text ": You can delete the API key you are currently using with this endpoint, so be careful!" a class: ".top", href: "#top", "back to top" a name: "errors" h2 "Errors" p "In addition to an array of errors (usually only consisting of one error message), the HTTP status returned will indicate errors:" ul -> li "400: Bad Request. This means something on the client-side did not call the API correctly." li "404: Not Found. This means the requested item doesn't exist. (For example, getting a task that has been deleted.)" li "500: Internal Server Error. Something went wrong with the server while processing your request." li -> text "501: Not Implemented. This error only exists for " code "/random" text ", the only part of the API not already implemented." p "Right now, please report any 500 errors you encounter. In the future, these will be logged automatically." a class: ".top", href: "#top", "back to top" a name: "rate-limit" h2 "Rate Limiting" p "At this time there is no rate limiting. This will be developed when needed, and the API documentation updated to reflect that, with at least 2 months' warning." a class: ".top", href: "#top", "back to top" a name: "changes" h2 "Changes" p "Recent additions (v1.1 I guess):" ul -> li -> text "Fixed a bug with " code "/get" text ", " code "/do" text ", and " code "/undo" text " not returning tasks (just success boolean or errors)." li -> text "Added " code "/edit" text " endpoint for modifying tasks." li -> text "Removed " code "id" text " from API keys, as they weren't working anyhow, and aren't neccesary." li "Added the ability to make requests using a URI and an authorization method only, which should make it easier to develop apps for. (More flexibility in how the API can be used.)" li "Extensively rewrote the API documentation. *wipes sweat from brow*" p "All API versions will be supported indefinitely, unless a new feature requires a backwards-incompatible change to the database. In that case, an API will be marked deprecated and will remain available for at least 6 months." p "All new features that do not require modifications to existing API calls will be added to v1 as they are introduced. The next version of the API will only exist if backwards-incompatible changes to the API itself are introduced." a class: ".top", href: "#top", "back to top" a name: "next" h2 "Coming Next" p "Until I implement an a tracking system for features/bugs, here is a brief list of features to be added 'soon', and a few known bugs:" ul -> li -> text "Implementing " code "/random" text " endpoint." li "Web interface utilizing edit endpoint for modifying tasks." li "Web interface using a paginated interface and sorting by incomplete tasks first." li -> code "/search" text " endpoint. The web interface will also be updated to utilize this." li "Lists (including the ability to have lists of lists). The API and web interface will receive several updates for this." li -> code "/duplicate" text " endpoint for duplicating a task or list (with optional inclusion of any sub-tasks/sub-lists). The web interface will be updated to utilize this." li "Web interface will dynamically create lists if you try to go to a URL specifying a list that doesn't exist." -- Note: Take parameter and slugify it, then create if it doesn't exist li "Tasks will be able to have parent tasks as well as being on a list." li "List option to only allow unique items on it." li "List option to not allow deleting items from it." li "CLI program for interacting with the API." li "Twitter integration." li -> text "Data export. (Note: Initially will be a large JSON dump, but you can contact me if you have special needs for data export and I will develop these upon request.)" ul -> li "Will be API-based, with options for which tasks to include or exclude, defaulting to exporting everything." p "And this is a list of ideas that may or may not be coming a bit later on, still being thought about:" ul -> li "Email integration." li "Reminders." li "Recurring tasks." li "Shared tasks." li -> text "Customization of web interface. (e.g. select between current interface and a kanban-style board)" ul -> li "This may be implemented based on both a default setting and allowing each list to specify what interface it uses." li "Reordering tasks/lists. (This one requires some research on my part of how to implement this efficiently.)" li "Tagging system (for tasks/lists)." li "Due dates/times." li "Webhooks." li "OAuth2 support." li "Import from other todo/task management software." p "Finally, this is a list of a few site-related things I need to get set up:" ul -> li "A homepage for non-users showing features / enticing sign-up. Of course. :P" li "A dynamically updated page showing project status, both in terms of budget (income/expenses) and features/bugs. (This will also allow users to vote for features/bugs to be prioritized.)" li "A page for accepting payments. At this stage, this is optional and I will be relying purely on people wanting to see this system succeed. In the future, more advanced API features may be behind a subscription model (intended to be $1.50/year)." a class: ".top", href: "#top", "back to top" a name: "notes" h2 "Notes / Tips" p "All routes are technically available by any HTTP verb. However, depending on the HTTP spec for the verb you use, this may cause required data to not be parsed correctly." p "I would recommend sticking to POST requests, but GET works for sure, and /new might work with anything if the content is specified in the URL." -- TODO clean up format here p "Do not write your code expecting ONLY the data specified here, as more features are added, more data will be in the task objects returned." a class: ".top", href: "#top", "back to top"