diff --git a/clients/emacs/bbj.el b/clients/emacs/bbj.el index 0650d1e..ee6572c 100644 --- a/clients/emacs/bbj.el +++ b/clients/emacs/bbj.el @@ -134,7 +134,7 @@ or can be ommitted to send no data." (re-search-forward "^$" nil t) (condition-case nil (setq json (json-read)) - (json-readtable-error + (error (user-error "BBJ response error")))) (case (setq error (alist-get 'error json) @@ -156,16 +156,18 @@ is the only thing returned (not the usermap or error field)" (defun bbj-sane-value (prompt key) "Opens an input loop with the user, where the response is passed to the server to check it for validity before the -user is allowed to continue." - (let (response value done) - (while (not done) - (setq value (read-from-minibuffer prompt) - response (bbj-data (bbj-request 'db_sanity_check - 'key key 'value value))) - (unless (setq done (alist-get 'bool response)) - (message (alist-get 'description response)) - (sit-for 2))) - value)) +user is allowed to continue. Will recurse until the input +is valid, then it is returned." + (let (response value) + (setq value (read-from-minibuffer prompt) + response (bbj-request! 'db_sanity_check + 'value value + 'key key)) + (if (alist-get 'bool response) + value + (message (alist-get 'description response)) + (sit-for 2) + (bbj-sane-value prompt key)))) (defun bbj-descend (alist &rest keys) @@ -195,11 +197,10 @@ You can restore anon status on demand by using `bbj-logout'" "user_name")) (cond ((bbj-request! 'user_is_registered 'target_user bbj-user) - (setq check - (bbj-request! 'check_auth - 'target_user bbj-user - 'target_hash (bbj-sethash))) - (if check (message "Logged in as %s!" bbj-user) + (if (bbj-request! 'check_auth + 'target_user bbj-user + 'target_hash (bbj-sethash)) + (message "Logged in as %s!" bbj-user) (setq bbj-hash nil) (if (y-or-n-p (format "Invalid credentials for %s. Try again? " bbj-user)) (bbj-login) diff --git a/documentation/VALIDATION.md b/documentation/VALIDATION.md new file mode 100644 index 0000000..6c1de78 --- /dev/null +++ b/documentation/VALIDATION.md @@ -0,0 +1,97 @@ +The server has an endpoint called `db_sanity_check`. What this does is take +a `key` and a `value` and compares `value` to a set of rules specified by +`key`. This is the same function used internally by the database to scan +values before committing them to the database. By default it returns a +descriptive object under `data`, but you can specify the key/value pair +`"error": True` to get a standard error response back. A standard call +to `db_sanity_check` will look like this. + +``` +{ + "key": "title", + "value": "this title\nis bad \nbecause it contains \nnewlines" +} +``` + +and the server will respond like this when the input should be corrected. + +``` +{ + "data": { + "bool": False, + "description": "Titles cannot contain whitespace characters besides spaces." + }, + "error": False, + "usermap": {} +} +``` + +if everything is okay, the data object will look like this instead. + +``` + "data": { + "bool": True, + "description": null + }, +``` + +Alternatively, you can supply `"error": True` in the request. + +``` +{ + "error": True, + "key": "title", + "value": "this title\nis bad \nbecause it contains \nnewlines" +} +// and you get... +{ + "data": null, + "usermap": {}, + "error": { + "code": 4, + "description": "Titles cannot contain whitespace characters besides spaces." + } +} +``` + +The following keys are currently available. + + * "user_name" + * "auth_hash" + * "quip" + * "bio" + * "title" + * "body" + * "color" + +The descriptions returned are friendly, descriptive, and should be shown +directly to users + +## Implementing good sanity checks in your client + +By using this endpoint, you will never have to validate values in your +own code before sending them to the server. This means you can do things +like implement an interactive prompt which will not allow the user to +submit it unless the value is correct. + +This is used in the elisp client when registering users and for the thread +title prompt which is shown before opening a composure window. The reason +for rejection is displayed clearly to the user and input window is restored. + +``` +(defun bbj-sane-value (prompt key) + "Opens an input loop with the user, where the response is +passed to the server to check it for validity before the +user is allowed to continue. Will recurse until the input +is valid, then it is returned." + (let (response value) + (setq value (read-from-minibuffer prompt) + response (bbj-request! 'db_sanity_check + 'value value + 'key key)) + (if (alist-get 'bool response) + value + (message (alist-get 'description response)) + (sit-for 2) + (bbj-sane-value prompt key)))) +``` diff --git a/src/db.py b/src/db.py index d41494b..f77a647 100644 --- a/src/db.py +++ b/src/db.py @@ -308,7 +308,7 @@ def validate(keys_and_values): elif contains_nonspaces(value): raise BBJUserError( - "Username cannot contain whitespace chars besides spaces.") + "Username cannot contain whitespace characters besides spaces.") elif not value.strip(): raise BBJUserError( @@ -330,7 +330,7 @@ def validate(keys_and_values): elif key == "quip": if contains_nonspaces(value): raise BBJUserError( - "Quip cannot contain whitespace chars besides spaces.") + "Quip cannot contain whitespace characters besides spaces.") elif len(value) > 120: raise BBJUserError( @@ -348,7 +348,7 @@ def validate(keys_and_values): elif contains_nonspaces(value): raise BBJUserError( - "Titles cannot contain whitespace chars besides spaces.") + "Titles cannot contain whitespace characters besides spaces.") elif not value.strip(): raise BBJUserError(