Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

gh-issues #4

Merged
merged 14 commits into from

2 participants

Raimon Grau Yann Hodique
Raimon Grau

That's a first minimal support for github issues. it allows creation, listing and updating of issues.
For the moment, there's no support for milestones, labels, nor assignees.

Feel free to rename variables, or add caching. I messed a bit with caching, without any success, so I left it out for the moment.

Comments are really appreciated.

gh-issues.el
((41 lines not shown))
+(require 'gh-common)
+
+(require 'gh-repos)
+
+(defclass gh-issues-api (gh-api-v3)
+ ((req-cls :allocation :class :initform gh-issues-issue))
+ "Github Issues api")
+
+(defclass gh-issues-issue (gh-object)
+ ((url :initarg :url)
+ (html-url :initarg :html-url)
+ (number :initarg :number)
+ (state :initarg :state)
+ (title :initarg :title)
+ (body :initarg :body)
+ (user :initarg :user :initform gh-user)
Yann Hodique Owner
sigma added a note

you should use separate slots for the user object, and for the class that's used to read it
hence
(user :initarg :user)
...
(user-cls :initarg :user-cls :initform gh-user)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
gh-issues.el
((43 lines not shown))
+(require 'gh-repos)
+
+(defclass gh-issues-api (gh-api-v3)
+ ((req-cls :allocation :class :initform gh-issues-issue))
+ "Github Issues api")
+
+(defclass gh-issues-issue (gh-object)
+ ((url :initarg :url)
+ (html-url :initarg :html-url)
+ (number :initarg :number)
+ (state :initarg :state)
+ (title :initarg :title)
+ (body :initarg :body)
+ (user :initarg :user :initform gh-user)
+ (labels :initarg :labels :initform nil)
+ (assignee :initarg :assignee :initform gh-user)
Yann Hodique Owner
sigma added a note

same here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
gh-issues.el
((44 lines not shown))
+
+(defclass gh-issues-api (gh-api-v3)
+ ((req-cls :allocation :class :initform gh-issues-issue))
+ "Github Issues api")
+
+(defclass gh-issues-issue (gh-object)
+ ((url :initarg :url)
+ (html-url :initarg :html-url)
+ (number :initarg :number)
+ (state :initarg :state)
+ (title :initarg :title)
+ (body :initarg :body)
+ (user :initarg :user :initform gh-user)
+ (labels :initarg :labels :initform nil)
+ (assignee :initarg :assignee :initform gh-user)
+ (milestone :initarg :milestone :initform gh-milestone)
Yann Hodique Owner
sigma added a note

same here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
gh-issues.el
((62 lines not shown))
+ (created_at :initarg :created_at)
+ (due_on :initarg :due_on))
+ "issues request")
+
+(defclass gh-issues-label (gh-object)
+ ((url :initarg :url)
+ (name :initarg :name)
+ (color :initarg :color)))
+
+(defclass gh-milestone (gh-object)
+ ((url :initarg :url)
+ (number :initarg :number)
+ (state :initarg :state)
+ (title :initarg :title)
+ (description :initarg :description)
+ (creator :initarg :creator :initform gh-user)
Yann Hodique Owner
sigma added a note

same here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
gh-issues.el
((82 lines not shown))
+ "github milestone")
+
+(defmethod gh-object-read-into ((issue gh-issues-issue) data)
+ (call-next-method)
+ (with-slots (url html-url number state title body
+ user labels assignee milestone open_issues
+ closed_issues created_at due_on)
+ issue
+ (setq url (gh-read data 'url)
+ url (gh-read data 'url)
+ html-url (gh-read data 'html-url)
+ number (gh-read data 'number)
+ state (gh-read data 'state)
+ title (gh-read data 'title)
+ body (gh-read data 'body)
+ user (gh-object-read (oref issue :user) (gh-read data 'user))
Yann Hodique Owner
sigma added a note

this and further occurrences need to be refactored after previous comments applied

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
gh-issues.el
((56 lines not shown))
+ (user :initarg :user :initform gh-user)
+ (labels :initarg :labels :initform nil)
+ (assignee :initarg :assignee :initform gh-user)
+ (milestone :initarg :milestone :initform gh-milestone)
+ (open_issues :initarg :open_issues)
+ (closed_issues :initarg :closed_issues)
+ (created_at :initarg :created_at)
+ (due_on :initarg :due_on))
+ "issues request")
+
+(defclass gh-issues-label (gh-object)
+ ((url :initarg :url)
+ (name :initarg :name)
+ (color :initarg :color)))
+
+(defclass gh-milestone (gh-object)
Yann Hodique Owner
sigma added a note

should be named gh-issues-milestone

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Raimon Grau

labels aren't implemented yet

Raimon Grau

These method is ugly because I try to protect assigning nil values on update

Raimon Grau

There's a problem with this current implementation. when creating a new milestone without some optional field, when doing
(oref milestone description) without it being initialized it doesn't return nil but throws an error. Is there any idiom for that? or just initialize these values in the constructor to nil?

Again, the method looks ugly to protect assigning nil on non-present values (when updating). Would it make sense to add a helper like gh-api-form-encode, that discards tuples where value == nil?

Yann Hodique sigma merged commit 4d5a482 into from
Yann Hodique
Owner

thanks, I merged it and made a couple of modifications

Raimon Grau

Great! There are still some rough edges here and there, and support for comments on issues. I hope I'll have time for them this week.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 311 additions and 0 deletions.
  1. +1 −0  .gitignore
  2. +309 −0 gh-issues.el
  3. +1 −0  gh.el
1  .gitignore
View
@@ -1,2 +1,3 @@
/docs/build/doctrees/
/docs/build/texinfo/
+TAGS
309 gh-issues.el
View
@@ -0,0 +1,309 @@
+;;; gh-issues.el --- issues api for github
+
+;; Copyright (C) 2012 Raimon Grau
+
+;; Author: Raimon Grau <raimonster@gmail.com>
+;; Keywords:
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Basic usage:
+
+;; (setf api (gh-issues-api "api" :sync nil :cache nil :num-retries 1))
+;; (setf issues (gh-issues-list api "user" "repo"))
+;; (last (oref issues data)) ; get one issue
+;; (setq mi (make-instance 'gh-issues-issue :body "issue body" :title "issue title" ))
+;; (gh-issues-issue-new api "user" "repo" mi)
+
+;;; Code:
+
+(eval-when-compile
+ (require 'cl))
+
+;;;###autoload
+(require 'eieio)
+
+(require 'gh-api)
+(require 'gh-auth)
+(require 'gh-common)
+
+(require 'gh-repos)
+
+(defclass gh-issues-api (gh-api-v3)
+ ((req-cls :allocation :class :initform gh-issues-issue)
+ (milestone-cls :allocation :class :initform gh-issues-milestone)
+ (label-cls :allocation :class :initform gh-issues-label)
+ )
+ "Github Issues api")
+
+(defclass gh-issues-issue (gh-object)
+ ((url :initarg :url)
+ (html-url :initarg :html-url)
+ (number :initarg :number)
+ (state :initarg :state)
+ (title :initarg :title)
+ (body :initarg :body)
+ (user :initarg :user :initform nil)
+ (labels :initarg :labels :initform nil)
+ (assignee :initarg :assignee :initform nil)
+ (milestone :initarg :milestone :initform nil)
+ (open_issues :initarg :open_issues)
+ (closed_issues :initarg :closed_issues)
+ (created_at :initarg :created_at)
+ (due_on :initarg :due_on)
+
+ (user-cls :allocation :class :initform gh-user)
+ (milestone-cls :allocation :class :initform gh-issues-milestone))
+ "issues request")
+
+(defclass gh-issues-label (gh-object)
+ ((url :initarg :url)
+ (name :initarg :name)
+ (color :initarg :color)))
+
+(defclass gh-issues-milestone (gh-object)
+ ((url :initarg :url)
+ (number :initarg :number)
+ (state :initarg :state)
+ (title :initarg :title)
+ (description :initarg :description)
+ (creator :initarg :creator :initform nil)
+ (open_issues :initarg :open_issues)
+ (closed_issues :initarg :closed_issues)
+ (created_at :initarg :created_at)
+ (due_on :initarg :due_on)
+
+ (user-cls :allocation :class :initform gh-user))
+ "github milestone")
+
+(defmethod gh-object-read-into ((issue gh-issues-issue) data)
+ (call-next-method)
+ (with-slots (url html-url number state title body
+ user labels assignee milestone open_issues
+ closed_issues created_at due_on)
+ issue
+ (setq url (gh-read data 'url)
+ html-url (gh-read data 'html-url)
+ number (gh-read data 'number)
+ state (gh-read data 'state)
+ title (gh-read data 'title)
+ body (gh-read data 'body)
+ user (gh-object-read (or
+ (oref issue :user)
+ (oref issue user-cls))
+ (gh-read data 'user))
+ labels (gh-read data 'labels)
+ assignee (gh-object-read (or
+ (oref issue :assignee)
+ (oref issue user-cls))
+ (gh-read data 'assignee))
+ milestone (gh-object-read (or
+ (oref issue :milestone)
+ (oref issue milestone-cls))
+ (gh-read data 'milestone))
+ open_issues (gh-read data 'open_issues)
+ closed_issues (gh-read data 'closed_issues)
+ created_at (gh-read data 'created_at)
+ due_on (gh-read data 'due_on))))
+
+
+(defmethod gh-object-read-into ((milestone gh-issues-milestone) data)
+ (call-next-method)
+ (with-slots (url number state title description creator
+ open_issues closed_issues
+ created_at due_on)
+ milestone
+ (setq url (gh-read data 'url)
+ number (gh-read data 'number)
+ state (gh-read data 'state)
+ title (gh-read data 'title)
+ description (gh-read data 'description)
+ creator (gh-object-read (or
+ (oref milestone :creator)
+ (oref milestone user-cls))
+ (gh-read data 'creator))
+
+ open_issues (gh-read data 'open_issues)
+ closed_issues (gh-read data 'closed_issues)
+ created_at (gh-read data 'created_at)
+ due_on (gh-read data 'due_on))))
+
+(defmethod gh-issues-issue-list ((api gh-issues-api) user repo)
+ (gh-api-authenticated-request
+ api (gh-object-list-reader (oref api req-cls)) "GET"
+ (format "/repos/%s/%s/issues" user repo)))
+
+(defmethod gh-issues-milestone-list ((api gh-issues-api) user repo)
+ (gh-api-authenticated-request
+ api (gh-object-list-reader (oref api milestone-cls)) "GET"
+ (format "/repos/%s/%s/milestones" user repo)))
+
+(defmethod gh-issues-milestone-get ((api gh-issues-api) user repo id)
+ (gh-api-authenticated-request
+ api (gh-object-reader (oref api milestone-cls)) "GET"
+ (format "/repos/%s/%s/milestones/%s" user repo id)))
+
+(defmethod gh-issues-milestone-new ((api gh-issues-api) user repo milestone)
+ (gh-api-authenticated-request
+ api (gh-object-reader (oref api milestone-cls)) "POST"
+ (format "/repos/%s/%s/milestones" user repo)
+ (gh-issues-milestone-req-to-update milestone)))
+
+(defmethod gh-issues-milestone-update ((api gh-issues-api) user repo id milestone)
+ (gh-api-authenticated-request
+ api (gh-object-reader (oref api milestone-cls)) "PATCH"
+ (format "/repos/%s/%s/milestones/%s" user repo id)
+ (gh-issues-milestone-req-to-update milestone)))
+
+(defmethod gh-issues-milestone-req-to-update ((milestone gh-issues-milestone))
+ (let ((state (oref milestone state) )
+ (description (oref milestone description))
+ (due_on (oref milestone due_on))
+ (to-update `(("title" . ,(oref milestone title)))))
+ (when state (nconc to-update `(("state" . ,state))))
+ (when description (nconc to-update `(("description" . ,description))))
+ (when due_on (nconc to-update `(("due_on" . ,due_on))))
+ to-update))
+
+(defmethod gh-issues-issue-get ((api gh-issues-api) user repo id)
+ (gh-authenticated-request
+ api (gh-object-reader (oref api req-cls)) "GET"
+ (format "/repos/%s/%s/issues/%s" user repo id)))
+
+(defmethod gh-issues-issue-req-to-update ((req gh-issues-issue))
+ (let ((assignee (oref req assignee))
+ ;; (labels (oref req labels))
+ (milestone (oref req milestone))
+ (to-update `(("title" . ,(oref req title))
+ ("state" . ,(oref req state))
+ ("body" . ,(oref req body)))))
+
+ ;; (when labels (nconc to-update `(("labels" . ,(oref req labels) ))))
+ (when milestone (nconc to-update `(("milestone" . ,(oref milestone number)))))
+ (when assignee (nconc to-update `(("assignee" . ,(oref assignee login) ))))
+ to-update))
+
+(defmethod gh-issues-issue-update ((api gh-issues-api) user repo id req)
+ (gh-api-authenticated-request
+ api (gh-object-reader (oref api req-cls)) "PATCH"
+ (format "/repos/%s/%s/issues/%s" user repo id)
+ (gh-issues-issue-req-to-update req)))
+
+(defmethod gh-issues-issue-new ((api gh-issues-api) user repo issue)
+ (gh-api-authenticated-request
+ api (gh-object-reader (oref api req-cls)) "POST"
+ (format "/repos/%s/%s/issues" user repo)
+ (gh-issues-issue-req-to-update issue)))
+
+;;; labels
+(defclass gh-issues-label (gh-object)
+ ((url :initarg :url)
+ (name :initarg :name)
+ (color :initarg :color)))
+
+(defmethod gh-object-read-into ((label gh-issues-label) data)
+ (call-next-method)
+ (with-slots (url name color)
+ label
+ (setq url (gh-read data 'url)
+ name (gh-read data 'name)
+ color (gh-read data 'color))))
+
+(defmethod gh-issues-label-req-to-update ((label gh-issues-label))
+ `(("name" . ,(oref label name))
+ ("color" . ,(oref label color))))
+
+(defmethod gh-issues-label-get ((api gh-issues-api) user repo name)
+ (gh-api-authenticated-request
+ api (gh-object-reader (oref api label-cls)) "GET"
+ (format "/repos/%s/%s/labels/%s" user repo name)))
+
+(defmethod gh-issues-label-list ((api gh-issues-api) user repo)
+ (gh-api-authenticated-request
+ api (gh-object-list-reader (oref api label-cls)) "GET"
+ (format "/repos/%s/%s/labels" user repo )))
+
+(defmethod gh-issues-label-new ((api gh-issues-api) user repo req)
+ (gh-api-authenticated-request
+ api (gh-object-reader (oref api label-cls)) "POST"
+ (format "/repos/%s/%s/labels" user repo)
+ (gh-issues-label-req-to-update req)))
+
+(defmethod gh-issues-label-update ((api gh-issues-api) user repo req)
+ (gh-api-authenticated-request
+ api (gh-object-reader (oref api label-cls)) "POST"
+ (format "/repos/%s/%s/labels/%s" user repo (oref req name))
+ (gh-issues-label-req-to-update req)))
+
+(defmethod gh-issues-label-delete ((api gh-issues-api) user repo name)
+ (gh-api-authenticated-request
+ api (gh-object-reader (oref api label-cls)) "DELETE"
+ (format "/repos/%s/%s/labels/%s" user repo name)))
+
+
+(defmethod gh-issues-labels-in-issue ((api gh-issues-api) user repo issue-or-issue-id)
+ (let ((issue-id (gh-issues--issue-id issue-or-issue-id)))
+ (gh-api-authenticated-request
+ api (gh-object-list-reader (oref api label-cls)) "GET"
+ (format "/repos/%s/%s/issues/%s/labels" user repo issue-id))))
+
+(defmethod gh-issues-labels-add-to-issue ((api gh-issues-api) user repo issue-or-issue-id labels)
+ (let ((issue-id (gh-issues--issue-id issue-or-issue-id)))
+ (gh-api-authenticated-request
+ api (gh-object-list-reader (oref api label-cls)) "PUT"
+ (format "/repos/%s/%s/issues/%s/labels" user repo issue-id)
+ (mapcar #'gh-issues--label-name labels))))
+
+(defmethod gh-issues-labels-remove-all-from-issue ((api gh-issues-api) user repo issue-or-issue-id )
+ (let ((issue-id (gh-issues--issue-id issue-or-issue-id)))
+ (gh-api-authenticated-request
+ api (lambda (x) x) "DELETE"
+ (format "/repos/%s/%s/issues/%s/labels" user repo issue-id))))
+
+(defmethod gh-issues-labels-in-milestone ((api gh-issues-api) user repo milestone-or-milestone-id)
+ (let ((milestone-id (gh-issues--milestone-id milestone-or-milestone-id)))
+ (gh-api-authenticated-request
+ api (gh-object-list-reader (oref api label-cls)) "GET"
+ (format "/repos/%s/%s/milestones/%s/labels" user repo milestone-id))))
+
+
+;;; helpers
+
+(defun gh-issues--issue-id (issue-or-issue-id)
+ (if (and (eieio-object-p issue-or-issue-id)
+ (eq (class-of issue-or-issue-id)
+ gh-issues-issue))
+ (oref issue-or-issue-id id)
+ issue-or-issue-id))
+
+(defun gh-issues--milestone-id (milestone-or-milestone-id)
+ (if (and (eieio-object-p milestone-or-milestone-id)
+ (eq (class-of milestone-or-milestone-id)
+ gh-issues-milestone))
+ (oref milestone-or-milestone-id id)
+ milestone-or-milestone-id))
+
+(defun gh-issues--label-name (label-or-label-name)
+ (if (and
+ (eieio-object-p label-or-label-name)
+ (eq (class-of label-or-label-name)
+ gh-issues-label))
+ (oref label-or-label-name name)
+ label-or-label-name))
+
+
+(provide 'gh-issues)
+;;; gh-issues.el ends here
1  gh.el
View
@@ -27,6 +27,7 @@
;;; Code:
(require 'gh-gist)
+(require 'gh-issues)
(defgroup gh nil
"Github support library."
Something went wrong with that request. Please try again.