From a1a5ccb76329d835adafdf117883199c92de44cb Mon Sep 17 00:00:00 2001 From: Jonathan Leech-Pepin Date: Fri, 14 Nov 2014 15:33:59 -0500 Subject: Updated `sx-request-make` to use a consistent format for both `POST` and `GET` methods. (sx-request-make): Added optional arguments for using "POST" and AUTH when performing requests (sx-request--build-keyword-arguments): Add option of using AUTH and including in query when required. (sx-request-build): Removed (sx-request--request): New function to perform query with all variables let bound. sx-auth.el: Updated `sx-auth-root` to be full auth URL rather than lack method. (sx-auth-authenticate): Remove dependency on `sx-request-build` and perform construction inline. --- sx-auth.el | 23 +++++++++++++---------- sx-request.el | 44 ++++++++++++++++++++++++-------------------- 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/sx-auth.el b/sx-auth.el index f32e7aa..4ccdd0e 100644 --- a/sx-auth.el +++ b/sx-auth.el @@ -28,7 +28,7 @@ (require 'sx-cache) (defconst sx-auth-root - "https://stackexchange.com/oauth/") + "https://stackexchange.com/oauth/dialog") (defconst sx-auth-redirect-uri "http://vermiculus.github.io/stack-mode/auth/auth.htm") (defconst sx-auth-client-id @@ -50,15 +50,18 @@ questions)." (interactive) (setq sx-auth-access-token - (let ((url (sx-request-build - "dialog" - `((client_id . ,sx-auth-client-id) - (scope . (read_inbox - no_expiry - write_access)) - (redirect_uri . ,(url-hexify-string - sx-auth-redirect-uri))) - "," sx-auth-root))) + (let ( + (url (concat + sx-auth-root + "?" + (sx-request--build-keyword-arguments + `((client_id . ,sx-auth-client-id) + (scope . (read_inbox + no_expiry + write_access)) + (redirect_uri . ,(url-hexify-string + sx-auth-redirect-uri))) + ",")))) (browse-url url) (read-string "Enter the access token displayed on the webpage: "))) (if (string-equal "" sx-auth-access-token) diff --git a/sx-request.el b/sx-request.el index 6dc54e7..da8e71e 100644 --- a/sx-request.el +++ b/sx-request.el @@ -71,19 +71,19 @@ number of requests left every time it finishes a call.") ;;; Making Requests (defun sx-request-make - (method &optional args silent) + (method &optional args use-auth use-post silent) (let ((url-automatic-caching sx-request-cache-p) (url-inhibit-uncompression t) (silent (or silent sx-request-silent-p)) - (call (sx-request-build - method - (cons (cons 'key sx-request-api-key) - args)))) + (request-method (if use-post "POST" "GET")) + (request-args + (sx-request--build-keyword-arguments args nil use-auth)) + (request-url (concat sx-request-api-root method))) (unless silent (sx-message "Request: %S" call)) - (let ((response-buffer (cond - ((equal '(24 . 4) (cons emacs-major-version emacs-minor-version)) - (url-retrieve-synchronously call silent)) - (t (url-retrieve-synchronously call))))) + (let ((response-buffer (sx-request--request request-url + request-args + request-method + silent))) (if (not response-buffer) (error "Something went wrong in `url-retrieve-synchronously'") (with-current-buffer response-buffer @@ -120,22 +120,26 @@ number of requests left every time it finishes a call.") ;;; Support Functions -(defun sx-request-build (method keyword-arguments &optional kv-value-sep root) - "Build the request string that will be used to process REQUEST -with the given KEYWORD-ARGUMENTS." - (let ((base (concat (or root sx-request-api-root) method)) - (args (sx-request--build-keyword-arguments - keyword-arguments kv-value-sep))) - (if (string-equal "" args) - base - (concat base "?" args)))) - -(defun sx-request--build-keyword-arguments (alist &optional kv-value-sep) +(defun sx-request--request (url args method silent) + (let ((url-request-method method) + (url-request-extra-headers + '(("Content-Type" . "application/x-www-form-urlencoded"))) + (url-request-data args)) + (cond + ((equal '(24 . 4) (cons emacs-major-version emacs-minor-version)) + (url-retrieve-synchronously url silent)) + (t (url-retrieve-synchronously url))))) + +(defun sx-request--build-keyword-arguments (alist &optional + kv-value-sep use-auth) "Build a \"key=value&key=value&...\"-style string with the elements of ALIST. If any value in the alist is `nil', that pair will not be included in the return. If you wish to pass a notion of false, use the symbol `false'. Each element is processed with `sx--thing-as-string'." + (when use-auth + (add-to-list 'alist (cons "key" sx-request-api-key)) + (add-to-list 'alist (car (sx-cache-get 'auth)))) (mapconcat (lambda (pair) (concat -- cgit v1.2.3 From 3593d62493b072b162abfec83a0e3081092738f0 Mon Sep 17 00:00:00 2001 From: Jonathan Leech-Pepin Date: Fri, 14 Nov 2014 15:56:01 -0500 Subject: Remove unneeded newline --- sx-auth.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sx-auth.el b/sx-auth.el index 4ccdd0e..b470523 100644 --- a/sx-auth.el +++ b/sx-auth.el @@ -50,8 +50,7 @@ questions)." (interactive) (setq sx-auth-access-token - (let ( - (url (concat + (let ((url (concat sx-auth-root "?" (sx-request--build-keyword-arguments -- cgit v1.2.3 From 97a0d11c657fcb3a7369e62ab4c7b5ee676444ea Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sat, 15 Nov 2014 17:29:40 -0500 Subject: Fix use of `rx' macro It needs to be evaluated. --- sx-question-mode.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 32cd112..03647bc 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -345,11 +345,11 @@ HEADER is given `sx-question-mode-header' face, and value is given FACE. (when sx-question-mode-bullet-appearance (font-lock-add-keywords ;; Bullet items. nil - `(((rx line-start (0+ blank) (group-n 1 (any "*+-")) blank) + `((,(rx line-start (0+ blank) (group-n 1 (any "*+-")) blank) 1 '(face nil display ,sx-question-mode-bullet-appearance) prepend)))) (font-lock-add-keywords ;; Highlight usernames. nil - `(((rx (or blank line-start) + `((,(rx (or blank line-start) (group-n 1 (and "@" (1+ (or (syntax word) (syntax symbol))))) symbol-end) 1 font-lock-builtin-face))) -- cgit v1.2.3 From 829821cc988be06c1f8a0699d11fecb7a6db972a Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sun, 16 Nov 2014 10:45:21 +0000 Subject: Implement sx-initialize --- sx.el | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/sx.el b/sx.el index 53aae84..64c555c 100644 --- a/sx.el +++ b/sx.el @@ -143,10 +143,19 @@ SETTER should be a function of two arguments. If SETTER is nil, (,(or setter #'setq) ,variable ,value)))) nil) -(defun stack-initialize () - (run-hooks - 'sx-init--internal-hook - 'sx-init-hook)) +(defvar sx-initialized nil + "Nil if sx hasn't been initialized yet. +If it has, holds the time at which initialization happened.") + +(defun sx-initialize (&optional force) + "Run initialization hooks if they haven't been run yet. +These are `sx-init--internal-hook' and `sx-init-hook'. +If FORCE is non-nil, run them even if they've already been run." + (when (or force (not sx-initialized)) + (prog1 + (run-hooks 'sx-init--internal-hook + 'sx-init-hook) + (setq sx-initialized (current-time))))) (provide 'sx) ;;; sx.el ends here -- cgit v1.2.3 From 14409012011d389c70ff79baf6dc3e228b08584c Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sun, 16 Nov 2014 10:45:46 +0000 Subject: Call sx-initialize on list-questions Addresses #63 --- sx-question-list.el | 1 + 1 file changed, 1 insertion(+) diff --git a/sx-question-list.el b/sx-question-list.el index b220097..72eabd3 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -323,6 +323,7 @@ focus the relevant window." (defun list-questions (no-update) "Display a list of StackExchange questions." (interactive "P") + (sx-initialize) (unless (buffer-live-p sx-question-list--buffer) (setq sx-question-list--buffer (generate-new-buffer "*question-list*"))) -- cgit v1.2.3 From d3222a36e8f225a3b3df13dbc6401673eff3474d Mon Sep 17 00:00:00 2001 From: Jonathan Leech-Pepin Date: Mon, 17 Nov 2014 07:45:53 -0500 Subject: Fix incorrect call to `call` rather than `request-url` --- sx-request.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sx-request.el b/sx-request.el index da8e71e..107806c 100644 --- a/sx-request.el +++ b/sx-request.el @@ -79,7 +79,7 @@ number of requests left every time it finishes a call.") (request-args (sx-request--build-keyword-arguments args nil use-auth)) (request-url (concat sx-request-api-root method))) - (unless silent (sx-message "Request: %S" call)) + (unless silent (sx-message "Request: %S" request-url)) (let ((response-buffer (sx-request--request request-url request-args request-method -- cgit v1.2.3 From a3d1ea047d0f4a5d4c4900b95de66955e65aa3d5 Mon Sep 17 00:00:00 2001 From: Jonathan Leech-Pepin Date: Mon, 17 Nov 2014 08:57:14 -0500 Subject: Adjust request keyword creation to verify if AUTH is needed. If `need-auth` is `'warn`, break with `user-error` to advise the user to authenticate. Otherwise the query will result in an `Error 401`. --- sx-request.el | 48 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/sx-request.el b/sx-request.el index 107806c..91c4db4 100644 --- a/sx-request.el +++ b/sx-request.el @@ -71,13 +71,13 @@ number of requests left every time it finishes a call.") ;;; Making Requests (defun sx-request-make - (method &optional args use-auth use-post silent) + (method &optional args need-auth use-post silent) (let ((url-automatic-caching sx-request-cache-p) (url-inhibit-uncompression t) (silent (or silent sx-request-silent-p)) (request-method (if use-post "POST" "GET")) (request-args - (sx-request--build-keyword-arguments args nil use-auth)) + (sx-request--build-keyword-arguments args nil need-auth)) (request-url (concat sx-request-api-root method))) (unless silent (sx-message "Request: %S" request-url)) (let ((response-buffer (sx-request--request request-url @@ -131,26 +131,42 @@ number of requests left every time it finishes a call.") (t (url-retrieve-synchronously url))))) (defun sx-request--build-keyword-arguments (alist &optional - kv-value-sep use-auth) + kv-value-sep need-auth) "Build a \"key=value&key=value&...\"-style string with the elements of ALIST. If any value in the alist is `nil', that pair will not be included in the return. If you wish to pass a notion of false, use the symbol `false'. Each element is processed with `sx--thing-as-string'." - (when use-auth + ;; Add API key to list of arguments, this allows for increased quota + ;; automatically. + (let* ((warn (equal need-auth 'warn)) + (auth + (let ((auth (car (sx-cache-get 'auth)))) + (cond + (auth) + ;; Pass user error when asking to warn + (warn + (user-error + "This query requires authentication. Please run `M-x sx-auth-authenticate' and try again.")) + ((not auth) + (lwarn "stack-mode" :debug + "This query requires authentication") + nil))))) (add-to-list 'alist (cons "key" sx-request-api-key)) - (add-to-list 'alist (car (sx-cache-get 'auth)))) - (mapconcat - (lambda (pair) - (concat - (sx--thing-as-string (car pair)) - "=" - (sx--thing-as-string (cdr pair) kv-value-sep))) - (delq nil (mapcar - (lambda (pair) - (when (cdr pair) pair)) - alist)) - "&")) + (if (and need-auth auth) + (add-to-list 'alist auth)) + (mapconcat + (lambda (pair) + (concat + (sx--thing-as-string (car pair)) + "=" + (sx--thing-as-string (cdr pair) kv-value-sep))) + (delq nil (mapcar + (lambda (pair) + (when (cdr pair) pair)) + alist)) + "&"))) + (provide 'sx-request) ;;; sx-request.el ends here -- cgit v1.2.3 From f4d7c0341ce30a93bc7d75591183292307971e41 Mon Sep 17 00:00:00 2001 From: Jonathan Leech-Pepin Date: Mon, 17 Nov 2014 09:26:27 -0500 Subject: Change from `add-to-list` to `push` to avoid a strange `(void-variable alist)` error. Moved `api-key` to let binding rather than directly in if statement. --- sx-request.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sx-request.el b/sx-request.el index 91c4db4..d982057 100644 --- a/sx-request.el +++ b/sx-request.el @@ -140,6 +140,7 @@ false, use the symbol `false'. Each element is processed with ;; Add API key to list of arguments, this allows for increased quota ;; automatically. (let* ((warn (equal need-auth 'warn)) + (api-key (cons "key" sx-request-api-key)) (auth (let ((auth (car (sx-cache-get 'auth)))) (cond @@ -152,9 +153,9 @@ false, use the symbol `false'. Each element is processed with (lwarn "stack-mode" :debug "This query requires authentication") nil))))) - (add-to-list 'alist (cons "key" sx-request-api-key)) + (push api-key alist) (if (and need-auth auth) - (add-to-list 'alist auth)) + (push auth alist)) (mapconcat (lambda (pair) (concat -- cgit v1.2.3 From 97eee0eae60c97bdab1361edb39dbac57c3dc6e5 Mon Sep 17 00:00:00 2001 From: Jonathan Leech-Pepin Date: Mon, 17 Nov 2014 10:48:32 -0500 Subject: Update `sx-method-call` to use `need-auth` and `use-post` variables when creating a request. --- sx-method.el | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sx-method.el b/sx-method.el index 6f0a36b..e9c4f60 100644 --- a/sx-method.el +++ b/sx-method.el @@ -29,9 +29,16 @@ (require 'sx-filter) (defun sx-method-call - (method &optional keyword-arguments filter silent) + (method &optional keyword-arguments filter need-auth use-post silent) "Call METHOD with KEYWORD-ARGUMENTS using FILTER. +If NEED-AUTH is non-nil, an auth-token is required. If 'WARN, +warn the user `(user-error ...)' if they do not have an AUTH +token set. + +If USE-POST is non-nil, use `POST' rather than `GET' for passing +arguments. + If SILENT is non-nil, no messages will be printed. Return the entire response as a complex alist." @@ -41,7 +48,9 @@ Return the entire response as a complex alist." (sx-filter-get-var (cond (filter filter) ((boundp 'stack-filter) stack-filter)))) - keyword-arguments))) + keyword-arguments) + need-auth + use-post)) (provide 'sx-method) ;;; sx-method.el ends here -- cgit v1.2.3