Gerrit Emacs Integration - Initial Project Prototyping
Added various functions to request resources Gerrit REST API and
organize data from responses for displaying comment reviews to Gerrit reviewees.
BUG=None
TEST=Ran code
Change-Id: I286ae1b3728be8533b95fc71aece7ce3f0dc76ed
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/dev-util/+/2272801
Reviewed-by: Jack Rosenthal <jrosenth@chromium.org>
Commit-Queue: Jack Rosenthal <jrosenth@chromium.org>
Tested-by: Jack Rosenthal <jrosenth@chromium.org>
diff --git a/contrib/emacs/gerrit.el b/contrib/emacs/gerrit.el
new file mode 100644
index 0000000..98857db
--- /dev/null
+++ b/contrib/emacs/gerrit.el
@@ -0,0 +1,79 @@
+;; -*- lexical-binding: t -*-
+
+;; Copyright 2020 The Chromium OS Authors. All rights reserved.
+;; Use of this source code is governed by a BSD-style license that can be
+;; found in the LICENSE file.
+(require 'request)
+
+;; TODO this is test code to be removed in future CL.
+(setq test-user "jrosenth")
+(setq test-host "chromium-review.googlesource.com")
+(setq test-repo-root (file-name-as-directory "~/chromiumos"))
+(setq test-repo-manifest-path (expand-file-name ".repo/manifests/default.xml" test-repo-root))
+
+(cl-defun gerrit--fetch-recent-changeid-project-pairs (host user &optional (count 3))
+ "Fetches recent changes as changeid project dotted pairs.
+host - Gerrit server address
+user - the user who owns the recent changes
+count (optional) - the number of recent changes, default is 3
+Fetch recent changes that are not abandoned/merged, and
+thus are actionable, returns a list of dotted pairs
+of the form (change-id . project)."
+ (let ((response
+ (request
+ (format "https://%s/changes/" host)
+ ;; We don't use "status:reviewed" because that only counts reviews after latest patch,
+ ;; but we may want reviews before the latest patch too.
+ :params `(("q" . ,(format "owner:%s status:open" user))
+ ("n" . ,(format "%d" count)))
+ :sync t
+ :parser 'gerrit--request-response-json-parser
+ :success (lambda (&key data error-thrown &allow-other-keys)
+ (when error-thrown
+ (message "%s" error-thrown))))))
+ (loop for change across (request-response-data response)
+ collect `(,(gethash "change_id" change) . ,(gethash "project" change)))))
+
+(defun gerrit--request-response-json-parser ()
+ "Response parsing callback for use with request.el
+parses Gerrit response json payload by removing the
+embedded XSS protection string before using a real json parser."
+ (json-parse-string (replace-regexp-in-string "^[[:space:]]*)]}'" "" (buffer-string))))
+
+
+(defun gerrit--get-unresolved-comments (host project change-id)
+ "Gets recent unresolved comments for open Gerrit CLs.
+Returns a map of the form path => sequence of comments,
+where path is the filepath from the gerrit project root
+and each comment represents a CommentInfo entity from Gerrit"
+ (let* ((response
+ (request
+ (format "https://%s/changes/%s~master~%s/comments"
+ host
+ (url-hexify-string project)
+ change-id)
+ :sync t
+ :parser 'gerrit--request-response-json-parser
+ :success (lambda (&key data error-thrown &allow-other-keys)
+ (when error-thrown
+ (message "%s" error-thrown)))))
+ (out-map (request-response-data response)))
+ ;; We only want the user to see unresolved comments.
+ (loop for key in (hash-table-keys out-map) do
+ ;; We explicitly check if not true because the value may be ':false'
+ ;; which is technically evals to true as it is not nil.
+ (delete-if (lambda (comment) (not (eql t (gethash "unresolved" comment))))
+ (gethash key out-map)))
+ out-map))
+
+
+(defun gerrit--fetch-map-changeid-project-pair-to-unresolved-comments (host user)
+ "Returns a map of maps of the form:
+(change-id . project) => filepath => Sequence(CommentInfo Map),
+where filepath is from the nearest git root for a file."
+ ;; The return value is intended as a local cache of comments for user's recent changes.
+ (let ((out-map (make-hash-table :test 'equal)))
+ (loop for pair in (gerrit--fetch-recent-changeid-project-pairs host user) do
+ (setf (gethash pair out-map)
+ (gerrit--get-unresolved-comments host (cdr pair) (car pair))))
+ out-map))