From 90eebfe3109994eb2359f70e4cbaaedb48e2d9be Mon Sep 17 00:00:00 2001 From: Colin Okay Date: Fri, 18 Mar 2022 16:45:53 -0500 Subject: Squashed commit of the following: commit e12b0ae984e5f106721ed866a96f918f19137295 Merge: 2cf6a98 d30575d Author: Colin Okay Date: Fri Mar 18 16:44:57 2022 -0500 Merge branch 'main' into rc-upstream commit 2cf6a9883b59fe27fc6cd4888261416a2e929be3 Author: Colin Okay Date: Tue Mar 15 16:33:56 2022 -0500 fixed odd help printout on `ol help` commit 3a19876d855ec8d2279293892e24716b28c4b48a Author: Colin Okay Date: Tue Mar 15 16:29:53 2022 -0500 added --confirm option. refactored run-item & co to accomodate commit fbce85871c6e995ece00e3136cf7dfdeeeff2bb7 Author: Colin Okay Date: Tue Mar 15 15:45:20 2022 -0500 bugfix: handle user interrupt in new and edit commit 130ae266f51f63ac423a65b17749a4d38dda1018 Author: Colin Okay Date: Mon Mar 14 19:36:18 2022 -0500 Added a --verbose / -v option to RUN commit 67c793633bfcd46997155341dd4b1776fb6a6728 Author: Colin Okay Date: Mon Mar 14 19:19:24 2022 -0500 Added help topic explaining icons in printouts commit 08fd723502bb3ee021911c0c9db0f743b575ad68 Author: Colin Okay Date: Mon Mar 14 19:06:36 2022 -0500 Added variables help topic commit 3c8b1804a7e520f465ced563bbf9b416b4abb8ab Author: Colin Okay Date: Mon Mar 14 19:00:54 2022 -0500 added EDITOR CONFIGURATION help topic commit 6dd2563a0f2b848e84f297a0a2296756c65f6246 Author: Colin Okay Date: Mon Mar 14 14:22:09 2022 -0500 made the note while hacking comment more visible commit 1a1de0bb8c18798064d103b70a1c06d20eb3ad05 Author: Colin Okay Date: Mon Mar 14 08:48:50 2022 -0500 moved host assertion inside handler-case commit e2091250cbc8dce679b52636647949d0b417e6be Author: Colin Okay Date: Mon Mar 14 08:21:12 2022 -0500 CONTRIBUTING rewording commit 86f7cdb2ce0f99aee0328617999f35b63694b4a6 Author: Colin Okay Date: Mon Mar 14 08:15:27 2022 -0500 argument checking for run and clip commit f18dddd766af9e06ba4f66487701322de495de59 Author: Colin Okay Date: Mon Mar 14 08:11:58 2022 -0500 tweaks to help menu printing commit 4b390fcde69013393bae325be2e8d2f5944cd15b Author: Colin Okay Date: Sun Mar 13 22:23:46 2022 -0500 tabulating help menu command listing commit 57bae08a8b9accc1d4bfb7165080e7d5a5ef2a30 Author: Colin Okay Date: Sun Mar 13 17:40:45 2022 -0500 deletion support; some cache syncing in client functions; commit 7a97e9d6ba5737f1088dd3a81b9b16121cf47c39 Author: Colin Okay Date: Sun Mar 13 14:45:28 2022 -0500 only print drafts when *drafts* is non null commit 6890b0349f79c0fbbbe5b8b7b3dbf8c2d2262e79 Author: Colin Okay Date: Sun Mar 13 14:43:59 2022 -0500 better printing of drafts commit 6a6c2ec8e2ef6cc9fa91f769d7dbe79387529619 Author: Colin Okay Date: Sun Mar 13 14:39:55 2022 -0500 printing drafts commit 8ba6552132fab6daef1f2b10adf932c87947e2ec Author: Colin Okay Date: Sun Mar 13 14:31:12 2022 -0500 can run the edit command on drafts with --redraft commit 5531288c433641d81d1359e0afa69f86d4bf5d07 Author: Colin Okay Date: Sun Mar 13 14:24:16 2022 -0500 can also edit drafts commit 7bcf634c789c00a95237ad74e5f923a4214020c8 Author: Colin Okay Date: Sun Mar 13 14:21:33 2022 -0500 can make, run, and publish drafts commit 7ae6cbff1875ea271fa1724d6e53bc3d3d48dd26 Author: Colin Okay Date: Sun Mar 13 13:56:49 2022 -0500 support for running drafts; drafts accounted for in with-local-state commit 07183b5bbb4d2e65514e3ec3e7cdf7e421f97749 Author: Colin Okay Date: Sun Mar 13 11:57:06 2022 -0500 added intial drafting code to new commit 80bf9816c6e35bf7ffcc1e4349d5abf056c4df7f Author: Colin Okay Date: Sun Mar 13 11:22:21 2022 -0500 updating helptexts; drafts-file function commit b5b0204d6742c68a90a6388593b980864ae301c3 Author: Colin Okay Date: Sun Mar 13 10:58:19 2022 -0500 removed old main commit 3236933be611f91183ea6b61890c7fbd0b892d8b Author: Colin Okay Date: Sun Mar 13 10:57:54 2022 -0500 removed spurious function commit 3a412cda20b45a2e6617d43e66c6c006924dad88 Author: Colin Okay Date: Sun Mar 13 10:57:05 2022 -0500 cosmetic change to command section printout commit 82a0911f93760a0059addc056340733f8797ae91 Author: Colin Okay Date: Sun Mar 13 10:46:41 2022 -0500 new main in app.lisp; exports to oneliners.cli package commit ebc79c386d4db262ae76bb2ba0386460a35648f9 Author: Colin Okay Date: Sun Mar 13 10:17:18 2022 -0500 tweaks to synopsis definition commit f39523d713c538f81d6f0cdc056fa750ae8a257e Author: Colin Okay Date: Sun Mar 13 09:58:09 2022 -0500 rewrote clon synopsis form --- lib/client.lisp | 255 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 174 insertions(+), 81 deletions(-) (limited to 'lib/client.lisp') diff --git a/lib/client.lisp b/lib/client.lisp index fe29932..61c3130 100644 --- a/lib/client.lisp +++ b/lib/client.lisp @@ -16,12 +16,16 @@ ;; along with this program. If not, see . (in-package :oneliners.cli) -;; NOTE WHILE HACKING. Each of the functions below that make HTTP -;; requests is meant to be called within the body of a -;; WITH-LOCAL-STATE form. If you are hacking in the REPL, make sure -;; to wrap function calls appropriately. +;;;; >>>>>NOTE WHILE HACKING<<<<<. +;;;; ---------------------------------------------------------------- +;;;; Each of the functions below that make HTTP requests are meant to +;;;; be called within the body of a WITH-LOCAL-STATE form. If you are +;;;; hacking in the REPL, make sure to wrap function calls +;;;; appropriately. +;;;; ---------------------------------------------------------------- -;;; GETTING ONELINERS & Displaying oneliners + +;;; GETTING ONELINERS & DISPLAYING ONELINERS (defun search-for-oneliners (terms limit &optional not-flagged-p all-flagged-p newestp) (assert (loop for term in terms never (find #\, term)) () "Search terms may not contain commas.") @@ -45,12 +49,18 @@ not in the local cache, try to fetch from configured server." (defmacro when-oneliner ((var name-or-id) &body body) "Finds the oneliner with name-or-id and binds it to var before running the body. If such a oneliner can be found." - (assert (symbolp var)) (let ((nvar (gensym))) `(let ((,nvar ,name-or-id)) (a:when-let (,var (the-oneliner ,nvar)) ,@body)))) +(defmacro when-draft ((var name) &body body) + "Like when-oneliner but restricts itself to local drafts." + (let ((nvar (gensym))) + `(let ((,nvar ,name)) + (a:when-let (,var (fetch-draft ,nvar)) + ,@body)))) + (defun newest-oneliners (&optional limit) (let ((response (if limit @@ -72,52 +82,62 @@ running the body. If such a oneliner can be found." (princ #\newline) (princ (oneliner-explanation ol))))) +(defun print-drafts () + (when *drafts* + (format t (concatenate 'string "~%~" (prin1-to-string *term-width*) "< ~;DRAFTS~; ~>~%")) + (dolist (draft *drafts*) + (print-oneliner-result-for-user (cdr draft))))) + ;;; RUNNING ONELINERS (defvar *ol-output-timeout* 1) -(defun run-item (ident args &key force-clip (timeout nil timeout-p)) - (when-oneliner (ol ident) - (let ((*ol-output-timeout* (if timeout-p timeout *ol-output-timeout*))) - (bind-vars-and-run-oneliner ol args force-clip)))) +(defun run-item (ident args &key force-clip (timeout nil timeout-p) draftp verbose confirm) + "Runs a oneliner identified by IDENT (if available) with arguments ARGS." + (let ((ol (if draftp (fetch-draft ident) (the-oneliner ident)))) + (when ol + (let ((*ol-output-timeout* (if timeout-p timeout *ol-output-timeout*))) + (bind-vars-and-run-oneliner ol args force-clip verbose confirm))))) -(defun bind-vars-and-run-oneliner (ol args &optional force-clip) +(defun bind-vars-and-run-oneliner (ol args &optional force-clip verbose confirm) (let* ((oneliner (oneliner-oneliner ol)) (runstyle (oneliner-runstyle ol)) (pos-args (get-positional-arguments ol)) (named-args (get-named-arguments ol))) + + (loop for param in pos-args + for arg in args + do (setf oneliner (str:replace-all param arg oneliner))) + ;; substitute named args + + (setf args + (mapcar (lambda (s) (str:split "=" s)) + (nthcdr (length pos-args) args))) + (loop for var in named-args + for bound = (assoc (subseq var 1) args :test #'equal) + when bound + do (setf oneliner + (str:replace-all var (second bound) oneliner))) + (when (or (not (oneliner-isflagged ol)) (y-or-n-p "This oneliner is flagged. Are you sure you want to run it?")) + (when (or verbose confirm) + (format t "Attempting to run:~%") + (princ oneliner) + (princ #\newline)) ;; substitute positional args - (loop for param in pos-args - for arg in args - do (setf oneliner (str:replace-all param arg oneliner))) - ;; substitute named args - (setf args - (mapcar (lambda (s) (str:split "=" s)) - (nthcdr (length pos-args) args))) - (loop for var in named-args - for bound = (assoc (subseq var 1) args :test #'equal) - when bound - do (setf oneliner - (str:replace-all var (second bound) oneliner))) - - (handle-run-oneliner oneliner (or force-clip (equalp runstyle "manual")))))) + (when (or (not confirm) + (y-or-n-p "Proceed?")) + (handle-run-oneliner oneliner (or force-clip (equalp runstyle "manual"))))))) (defun handle-run-oneliner (ol &optional clip) (if clip (progn (trivial-clipboard:text ol) (format t "Copied oneliner to clipboard~%")) - (progn - (format t "Attempting to run:~%") - (princ ol) - (princ #\newline) - (princ #\newline) - (run-with-shell ol :shell-name (or (shell) "bash") :await-output-p *ol-output-timeout*)))) + (run-with-shell ol :shell-name (or (shell) "bash") :await-output-p *ol-output-timeout*))) ;;; ADDING ONELINERS - (defun add-new-oneliner () (api-token) ;; fails with error if not set. ;; read each field required to make a onelienr in from a prompt. @@ -131,8 +151,18 @@ running the body. If such a oneliner can be found." (prompt "Name (leave blank for none): " :expect 'valid-oneliner-name-p :retry-text "Must begin with a letter contain only letters, numbers, - and _."))) + + (draft-name + (unless (y-or-n-p "Upload immediately instead of keeping a draft?") + (if (plusp (length name)) + name + (prompt "No name was provided, name this draft: " + :expect 'valid-oneliner-name-p + :retry-text "Must begin with a letter contain only letters, numbers, - and _.")))) + (init-tags (parse-oneliner-tags oneliner-string)) + (brief (prompt "Brief Description: " :expect 'valid-brief-description-p @@ -153,25 +183,34 @@ running the body. If such a oneliner can be found." (when (y-or-n-p "Provide an explanation?") (string-from-editor (format nil "~a~%~%" oneliner-string))))) - (api:request-with - (:body (jonathan:to-json - (list :oneliner oneliner-string - :name (if (plusp (length name)) name :null) - :tags tags - :brief brief - :explanation explanation - :runstyle runstyle)) - :content-type "application/json") - (api:post--oneliner :token (api-token)) ;TODO: update api to return the instance created. - (format t "Added~%")))) + (let ((local + (make-oneliner + :oneliner oneliner-string + :name name + :tags tags + :brief brief + :explanation explanation + :runstyle runstyle))) + (if draft-name + ;; if this is a draft, save it to disk. + (progn + (put-draft draft-name local) + (format t "Saved draft ~a~%Do `ol --draft run ~a` to test~%" + draft-name + draft-name)) + ;; otherwise, format the oneliner as json and make a request + ;; to create a new oneliner in the wiki + (api:request-with + (:body (oneliner-to-json-body local) + :content-type "application/json") + (api:post--oneliner :token (api-token)) ;TODO: update api to return the instance created. + (format t "Added Oneliner~%")))))) ;;; EDITING ONELINERS -(defun edit-item (ident) +(defun edit-item (ident &optional draftp) (api-token) ;; fails with error if not set. - (when-oneliner (ol ident) - ;; Like adding a oneliner, read each field in from a prompt. - ;; Here, prefil the field with its current value. + (let ((ol (if draftp (fetch-draft ident) (the-oneliner ident)))) (let* ((oneliner-string (prompt "Oneliner: " :expect 'valid-oneliner-string-p @@ -184,13 +223,23 @@ running the body. If such a oneliner can be found." :expect 'valid-oneliner-name-p :retry-text "Must begin with a letter contain only letters, numbers, - and _." :prefill (or (oneliner-name ol) "")))) + + (draft-name + (unless (y-or-n-p "Upload edits immediately instead of keeping a draft?") + (if (plusp (length name)) + name + (prompt "No name was provided, name this draft: " + :expect 'valid-oneliner-name-p + :retry-text "Must begin with a letter contain only letters, numbers, - and _.")))) (brief (prompt "Brief Description: " :expect 'valid-brief-description-p :retry-text "Too long. Must be <= 72 characters: " :prefill (oneliner-brief ol))) + (init-tags (parse-oneliner-tags oneliner-string)) + (tags (progn (format t "Tags include: ~{~a ~}~%" init-tags) @@ -208,49 +257,93 @@ running the body. If such a oneliner can be found." :expect 'valid-runstyle-p :retry-text "Must be (auto or manual): " :prefill (oneliner-runstyle ol)))) + (explanation - (when (y-or-n-p "Provide an explanation?") + (when (y-or-n-p "Alter the explanation?") (string-from-editor (oneliner-explanation ol))))) - (let ((new-item - (list :oneliner oneliner-string - :tags tags - :brief brief - :name (if (plusp (length name)) name :null) - :explanation explanation - :runstyle runstyle))) - (api:request-with - (:body (jonathan:to-json new-item) - :content-type "application/json") + + (let ((local + (make-oneliner + :id (oneliner-id ol) + :oneliner oneliner-string + :name (if (plusp (length name)) name :null) + :tags tags + :brief brief + :explanation explanation + :runstyle runstyle))) + (if draft-name + (progn + (put-draft draft-name local) + (format t "Saved draft ~a~%Do `ol --draft run ~a` to test~%" + draft-name + draft-name)) + (api:request-with + (:body (oneliner-to-json-body local) + :content-type "application/json") + (api:patch--oneliner-entry-edit (oneliner-id ol) :token (api-token)) + ;(merge-oneliners (list new-item)) ;;TODO: this is broken, wait for API update. + (format t "Edits accepted~%"))))))) + +;;TODO: need to sync cache here. +(defun publish-draft (name) + (when-draft (ol name) + (api:request-with + (:body (oneliner-to-json-body ol) + :content-type "application/json") + (if (oneliner-id ol) (api:patch--oneliner-entry-edit (oneliner-id ol) :token (api-token)) - (merge-oneliners (list new-item)) ;;TODO: this is broken, wait for API update. - (format t "OK~%")))))) + (api:post--oneliner :token (api-token))) + ;; if that worked, no http error occured, so this next part will run + (drop-draft name) + (format t "Draft ~a published and removed from drafts.~%" name)))) ;;; ADMIN OF ONELINER ENTRIES +(defun delete-item (ident) + (when-oneliner (ol ident) + (api:delete--oneliner-oneliner + ident + :token (api-token)) + ;; if we've made it this far no http error has been returned, + ;; hence we can delete it from the cache + (remove-from-cache ident))) + (defun flag-item (ident) + (when-oneliner (ol ident) + (api:put--oneliner-entry-flag + ident + :token (api-token) + :value "true") + ;; no http error, so we flag the cached version, ol. + (setf (oneliner-isflagged ol) t))) + +(defun unflag-item (ident) + (when-oneliner (ol ident) + (api:put--oneliner-entry-flag + ident + :token (api-token) + :value "false") + ;; no http error, so we can unflag the cached version, ol + (setf (oneliner-isflagged ol) nil))) + +(defun lock-item (ident) + (when-oneliner (ol ident) + (api:put--oneliner-oneliner-locked + ident + :token (api-token) + :value "true") + ;; no http error, so we can lock the cached version, ol + (setf (oneliner-islocked ol) t))) + +(defun unlock-item (ident) (when-oneliner (ol ident) - (api:put--oneliner-entry-flag (oneliner-id ol) - :token (api-token) - :value "true"))) - -(defun unflag-item (item-number) - (when-oneliner (ol item-number) - (api:put--oneliner-entry-flag (oneliner-id ol) - :token (api-token) - :value "false"))) - -(defun lock-item (item-number) - (when-oneliner (ol item-number) - (api:put--oneliner-oneliner-locked (oneliner-id ol) - :token (api-token) - :value "true"))) - -(defun unlock-item (item-number) - (when-oneliner (ol item-number) - (api:put--oneliner-oneliner-locked (oneliner-id ol) - :token (api-token) - :value "false"))) + (api:put--oneliner-oneliner-locked + ident + :token (api-token) + :value "false") + ;; no http error, so we can unlock the cached version, ol + (setf (oneliner-islocked ol) nil))) ;;; ACCOUNT AND INVITE STUFF -- cgit v1.2.3