(defpackage #:lazybones-client.shared (:intern #:req-string) (:export #:*host* #:*body* #:*headers* #:*cookies*)) (defpackage #:ONELINERS.API-CLIENT (:use :cl :lazybones-client.shared) (:export #:*host* #:*body* #:*headers* #:*cookies* #:request-with #:GET--SEARCH #:PATCH--FLAG-ONELINER #:PATCH--EDIT-ONELINER #:PATCH--UNLOCK-ONELINER #:PATCH--LOCK-ONELINER #:POST--ADD-ONELINER #:POST--MAKE-INVITE #:POST--REVOKE-CONTRIBUTOR #:POST--TOKEN-CONTRIBUTOR #:POST--REDEEM-INVITE)) (in-package :ONELINERS.API-CLIENT) (defvar *host* nil "The host to which the client will send its requests.") (defvar *body* nil "Body passed to client post, put, and patch requests") (defvar *cookies* nil "An instance of CL-COOKIE:COOKIE-JAR.") (defvar *headers* nil "A liist of (header-name . header-value) pairs.") (defmacro request-with ((&key host body headers content-type cookies) &body forms) "Make a request in a specific context. HOST is a string, the hostname where the request will be sent. Defaults to *HOST*. BODY should be a string, an alist, or a pathname. Default to *BODY* HEADERS should be an ALIST of (header-name . header-value) string pairs. Defaults to *HEADERS*. CONTENT-TYPE is a convenience for supplying just the Content-Type header. COOKIES should be an instance of CL-COOKIE:COOKIE-JAR. Defaults to *COOKIES*. " (let ((content-type-var (gensym)) (http-error-var (gensym))) `(let ((*host* (or ,host *host*)) (*body* (or ,body *body*)) (*headers* (or ,headers *headers*)) (*cookies* (or ,cookies *cookies*)) (,content-type-var ,content-type)) (when ,content-type-var (push (cons "Content-Type" ,content-type-var) *headers*)) (handler-case (progn ,@forms) (dex:http-request-failed (,http-error-var) (format *error-output* "~a -- ~a" (dex:response-status ,http-error-var) (dex:response-body ,http-error-var))))))) (DEFUN GET--SEARCH (&KEY TAGS LIMIT NOTFLAGGED) "A search endpoint returning a JSON encoded array of Oneliner Entries. TAGS cannot be empty. Returns a [Search Result](#search-result) object." (LET ((LAZYBONES-CLIENT.SHARED::REQ-STRING (APPLY #'CONCATENATE 'STRING LAZYBONES-CLIENT.SHARED:*HOST* (FORMAT NIL "/search") (WHEN (OR TAGS LIMIT NOTFLAGGED) (LIST "?" (IF TAGS (CONCATENATE 'STRING (SYMBOL-NAME 'TAGS) "=" (FORMAT NIL "~a" TAGS)) "") (IF LIMIT (CONCATENATE 'STRING "&" (SYMBOL-NAME 'LIMIT) "=" (FORMAT NIL "~a" LIMIT)) "") (IF NOTFLAGGED (CONCATENATE 'STRING "&" (SYMBOL-NAME 'NOTFLAGGED) "=" (FORMAT NIL "~a" NOTFLAGGED)) "")))))) (DEXADOR:GET LAZYBONES-CLIENT.SHARED::REQ-STRING :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*))) (DEFUN PATCH--FLAG-ONELINER (ONELINER) "Flag the oneliner for review. Open to anyone." (LET ((LAZYBONES-CLIENT.SHARED::REQ-STRING (APPLY #'CONCATENATE 'STRING LAZYBONES-CLIENT.SHARED:*HOST* (FORMAT NIL "/flag/~a" ONELINER) NIL))) (IF LAZYBONES-CLIENT.SHARED:*BODY* (DEXADOR:PATCH LAZYBONES-CLIENT.SHARED::REQ-STRING :CONTENT LAZYBONES-CLIENT.SHARED:*BODY* :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*) (DEXADOR:PATCH LAZYBONES-CLIENT.SHARED::REQ-STRING :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*)))) (DEFUN PATCH--EDIT-ONELINER (ONELINER) "Edit the fields of a oneliner." (LET ((LAZYBONES-CLIENT.SHARED::REQ-STRING (APPLY #'CONCATENATE 'STRING LAZYBONES-CLIENT.SHARED:*HOST* (FORMAT NIL "/edit/~a" ONELINER) NIL))) (IF LAZYBONES-CLIENT.SHARED:*BODY* (DEXADOR:PATCH LAZYBONES-CLIENT.SHARED::REQ-STRING :CONTENT LAZYBONES-CLIENT.SHARED:*BODY* :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*) (DEXADOR:PATCH LAZYBONES-CLIENT.SHARED::REQ-STRING :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*)))) (DEFUN PATCH--UNLOCK-ONELINER (ONELINER) "Unlocks a oneliner." (LET ((LAZYBONES-CLIENT.SHARED::REQ-STRING (APPLY #'CONCATENATE 'STRING LAZYBONES-CLIENT.SHARED:*HOST* (FORMAT NIL "/unlock/~a" ONELINER) NIL))) (IF LAZYBONES-CLIENT.SHARED:*BODY* (DEXADOR:PATCH LAZYBONES-CLIENT.SHARED::REQ-STRING :CONTENT LAZYBONES-CLIENT.SHARED:*BODY* :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*) (DEXADOR:PATCH LAZYBONES-CLIENT.SHARED::REQ-STRING :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*)))) (DEFUN PATCH--LOCK-ONELINER (ONELINER) "Locks a oneliner. Locked oneliners cannot be edited or flagged." (LET ((LAZYBONES-CLIENT.SHARED::REQ-STRING (APPLY #'CONCATENATE 'STRING LAZYBONES-CLIENT.SHARED:*HOST* (FORMAT NIL "/lock/~a" ONELINER) NIL))) (IF LAZYBONES-CLIENT.SHARED:*BODY* (DEXADOR:PATCH LAZYBONES-CLIENT.SHARED::REQ-STRING :CONTENT LAZYBONES-CLIENT.SHARED:*BODY* :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*) (DEXADOR:PATCH LAZYBONES-CLIENT.SHARED::REQ-STRING :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*)))) (DEFUN POST--ADD-ONELINER () "Make a new [oneliner](#oneliner)." (LET ((LAZYBONES-CLIENT.SHARED::REQ-STRING (APPLY #'CONCATENATE 'STRING LAZYBONES-CLIENT.SHARED:*HOST* (FORMAT NIL "/add-oneliner") NIL))) (IF LAZYBONES-CLIENT.SHARED:*BODY* (DEXADOR:POST LAZYBONES-CLIENT.SHARED::REQ-STRING :CONTENT LAZYBONES-CLIENT.SHARED:*BODY* :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*) (DEXADOR:POST LAZYBONES-CLIENT.SHARED::REQ-STRING :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*)))) (DEFUN POST--MAKE-INVITE () "On success, return an object containing a new [invite token](#invite-token)." (LET ((LAZYBONES-CLIENT.SHARED::REQ-STRING (APPLY #'CONCATENATE 'STRING LAZYBONES-CLIENT.SHARED:*HOST* (FORMAT NIL "/make-invite") NIL))) (IF LAZYBONES-CLIENT.SHARED:*BODY* (DEXADOR:POST LAZYBONES-CLIENT.SHARED::REQ-STRING :CONTENT LAZYBONES-CLIENT.SHARED:*BODY* :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*) (DEXADOR:POST LAZYBONES-CLIENT.SHARED::REQ-STRING :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*)))) (DEFUN POST--REVOKE-CONTRIBUTOR (CONTRIBUTOR) "A contributor can revoke their own access (if for some reason their API key ends up out of their control), or an admin can revoke anybody's access token, forcing the to re-authenticate." (LET ((LAZYBONES-CLIENT.SHARED::REQ-STRING (APPLY #'CONCATENATE 'STRING LAZYBONES-CLIENT.SHARED:*HOST* (FORMAT NIL "/revoke/~a" CONTRIBUTOR) NIL))) (IF LAZYBONES-CLIENT.SHARED:*BODY* (DEXADOR:POST LAZYBONES-CLIENT.SHARED::REQ-STRING :CONTENT LAZYBONES-CLIENT.SHARED:*BODY* :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*) (DEXADOR:POST LAZYBONES-CLIENT.SHARED::REQ-STRING :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*)))) (DEFUN POST--TOKEN-CONTRIBUTOR (CONTRIBUTOR &KEY PASSWORD) "Authenticate a contributor and reply with an [api token](#access-token)" (LET ((LAZYBONES-CLIENT.SHARED::REQ-STRING (APPLY #'CONCATENATE 'STRING LAZYBONES-CLIENT.SHARED:*HOST* (FORMAT NIL "/token/~a" CONTRIBUTOR) (WHEN (OR PASSWORD) (LIST "?" (IF PASSWORD (CONCATENATE 'STRING (SYMBOL-NAME 'PASSWORD) "=" (FORMAT NIL "~a" PASSWORD)) "")))))) (IF LAZYBONES-CLIENT.SHARED:*BODY* (DEXADOR:POST LAZYBONES-CLIENT.SHARED::REQ-STRING :CONTENT LAZYBONES-CLIENT.SHARED:*BODY* :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*) (DEXADOR:POST LAZYBONES-CLIENT.SHARED::REQ-STRING :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*)))) (DEFUN POST--REDEEM-INVITE (INVITE &KEY USERNAME PASSWORD1 PASSWORD2) "Redeem an [invite code](#invite-code) and create a new [contributor](#new-contributor-post-body)" (LET ((LAZYBONES-CLIENT.SHARED::REQ-STRING (APPLY #'CONCATENATE 'STRING LAZYBONES-CLIENT.SHARED:*HOST* (FORMAT NIL "/redeem/~a" INVITE) (WHEN (OR USERNAME PASSWORD1 PASSWORD2) (LIST "?" (IF USERNAME (CONCATENATE 'STRING (SYMBOL-NAME 'USERNAME) "=" (FORMAT NIL "~a" USERNAME)) "") (IF PASSWORD1 (CONCATENATE 'STRING "&" (SYMBOL-NAME 'PASSWORD1) "=" (FORMAT NIL "~a" PASSWORD1)) "") (IF PASSWORD2 (CONCATENATE 'STRING "&" (SYMBOL-NAME 'PASSWORD2) "=" (FORMAT NIL "~a" PASSWORD2)) "")))))) (IF LAZYBONES-CLIENT.SHARED:*BODY* (DEXADOR:POST LAZYBONES-CLIENT.SHARED::REQ-STRING :CONTENT LAZYBONES-CLIENT.SHARED:*BODY* :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*) (DEXADOR:POST LAZYBONES-CLIENT.SHARED::REQ-STRING :COOKIE-JAR LAZYBONES-CLIENT.SHARED:*COOKIES* :HEADERS LAZYBONES-CLIENT.SHARED:*HEADERS*))))