dakrone / es-mode

An Emacs major mode for interacting with Elasticsearch
GNU General Public License v3.0
195 stars 34 forks source link

org-babel: parse `:var` header args #82

Open qzdl opened 2 years ago

qzdl commented 2 years ago

Hey, thanks for a lovely experience with elasticsearch.

I've been getting into #+call blocks recently, and a killer feature has been the use of :var, which turns into the backend-specific variable (usually inlined before the declaration)

I have a hacky implementation that you can try here, if you're interested I can make it work for the multi requests and submit a PR.

Cheers!

(defun org-babel-execute:es (body params)
  "Execute a block containing an Elasticsearch query with
org-babel.  This function is called by
`org-babel-execute-src-block'. If `es-warn-on-delete-query' is
set to true, this function will also ask if the user really wants
to do that."
  (with-temp-buffer
    (es-mode)
    (setq es-request-method (upcase (or (cdr (assoc :method params)) es-default-request-method)))
    (setq es-endpoint-url (or (cdr (assoc :url params)) es-default-url))
    (message "es-mode: :var params: %s" (org-babel--get-vars params))
    (message "es-mode: body: %s" (es-get-request-body)) 
    (insert (org-babel-expand-body:es body params))
    (beginning-of-buffer)
    (let* ((headers (es-org--parse-headers (cdr (assoc :headers params))))
           (request (es-get-request-body))
           (vars (org-babel--get-vars params))
           (_ ;; setup arg parsing for `request'
            (mapcar
             (lambda (p)
               (cl-labels ((value (v) (if (symbolp v)
                                          (symbol-name v)
                                        ;(string-replace "\\" "\\\\" v) ; oof
                                        v
                                        ))) 
                 (cl-destructuring-bind (var . arg) p
                   (setq request
                         (string-replace (concat "$" (value var))
                                         (value arg)         
                                         request))
                   (message request))))
             vars))
           (output (es-org-execute-request
                    (cdr (assoc :jq params))
                    (cdr (assoc :tablify params))
                    request
                    headers))
           (file (cdr (assoc :file params))))
      (ignore-errors
        (while (es-goto-next-request)
          (setq output
                (concat output
                        "\n"
                        (es-org-execute-request
                         (cdr (assoc :jq params))
                         (cdr (assoc :tablify params))
                         (es-get-request-body)
                         headers)))))
      (if file
          (with-current-buffer (find-file-noselect file)
            (delete-region (point-min) (point-max))
            (if (string-suffix-p ".org" file t)
                (progn (require 'org-json)
                       (insert (org-json-decode (json-read-from-string output) 1)))
              (insert output))
            (save-buffer))
        output))))
dakrone commented 2 years ago

@qzdl thanks for taking a look, I am totally open to supporting variables this way, would you be interested in working on a PR that adds this? (and maybe some docs for users also)

qzdl commented 2 years ago

sure thing! I'll prepare some contextual info too, the tldr is that org-babel-ref-resolve is used intermittently in other libraries, and in varying ways, and most arg parsing lets the native backend handle the variable substitution, which might make better UX

;; prefer this
#+call: my-jq-block(query="\"term1\" AND \"term2\"")
;; to this
#+call: my-jq-block(query="\\\"term1\\\" AND \\\"term2\\\"")