import { VssueAPI } from 'vssue' ; import axios , { AxiosInstance , AxiosRequestConfig } from 'axios' ; import { buildURL , concatURL , getCleanURL , parseQuery } from '@vssue/utils' ; import { normalizeUser , normalizeIssue , normalizeComment , normalizeReactions , mapReactionName , } from './utils' ; import { ResponseAccessToken , ResponseUser , ResponseIssue , ResponseComment , ResponseReaction , ResponseSearch , } from './types' ; see https see https export default class GithubV3 implements VssueAPI . Instance { baseURL : string ; owner : string ; repo : string ; labels : Array < string > ; clientId : string ; clientSecret : string ; state : string ; proxy : string | ( ( url : string ) => string ) ; $http : AxiosInstance ; constructor ( { baseURL = 'https://github.com' , owner , repo , labels , clientId , clientSecret , state , proxy , } ) { if ( typeof clientSecret === 'undefined' || typeof proxy === 'undefined' ) { throw new Error ( 'clientSecret and proxy is required for GitHub V3' ) ; } this . baseURL = baseURL ; this . owner = owner ; this . repo = repo ; this . labels = labels ; this . clientId = clientId ; this . clientSecret = clientSecret ; this . state = state ; this . proxy = proxy ; this . $http = axios . create ( { baseURL : baseURL === 'https://github.com' ? 'https://api.github.com' : concatURL ( baseURL , 'api/v3' ) , headers : { Accept : 'application/vnd.github.v3+json' , } , } ) ; this . $http . interceptors . response . use ( response => { if ( response . data && response . data . error ) { return Promise . reject ( new Error ( response . data . error_description ) ) ; } return response ; } , error => { if ( typeof error . response === 'undefined' && error . message === 'Network Error' ) { error . response = { status : 403 , } ; } return Promise . reject ( error ) ; } ) ; } /**
     * The platform api info
     */ get platform ( ) : VssueAPI . Platform { return { name : 'GitHub' , link : this . baseURL , version : 'v3' , meta : { reactable : true , sortable : false , } , } ; } see https redirectAuth ( ) { window . location . href = buildURL ( concatURL ( this . baseURL , 'login/oauth/authorize' ) , { client_id : this . clientId , redirect_uri : window . location . href , scope : 'public_repo' , state : this . state , } ) ; } see https remarks async handleAuth ( ) { const query = parseQuery ( window . location . search ) ; if ( query . code ) { if ( query . state !== this . state ) { return null ; } const code = query . code ; delete query . code ; delete query . state ; const replaceURL = buildURL ( getCleanURL ( window . location . href ) , query ) + window . location . hash ; window . history . replaceState ( null , '' , replaceURL ) ; const accessToken = await this . getAccessToken ( { code } ) ; return accessToken ; } return null ; } see https async getAccessToken ( { code } ) { see https const originalURL = concatURL ( this . baseURL , 'login/oauth/access_token' ) ; const proxyURL = typeof this . proxy === 'function' ? this . proxy ( originalURL ) : this . proxy ; const { data } = await this . $http . post < ResponseAccessToken > ( proxyURL , { client_id : this . clientId , client_secret : this . clientSecret , code , } , { headers : { Accept : 'application/json' , } , } ) ; return data . access_token ; } see https async getUser ( { accessToken , } ) { const { data } = await this . $http . get < ResponseUser > ( 'user' , { headers : { Authorization : `token ${ accessToken }` } , } ) ; return normalizeUser ( data ) ; } see https see https see https see https async getIssue ( { accessToken , issueId , issueTitle , } ) { const options = { } ; if ( accessToken ) { options . headers = { Authorization : `token ${ accessToken }` , } ; } if ( issueId ) { try { options . params = { timestamp : Date . now ( ) , } ; const { data } = await this . $http . get < ResponseIssue > ( `repos/${ this . owner }/${ this . repo }/issues/${ issueId }` , options ) ; return normalizeIssue ( data ) ; } catch ( e ) { if ( e . response && e . response . status === 404 ) { return null ; } else { throw e ; } } } else { options . params = { q : [ `"${ issueTitle }"` , `is:issue` , `in:title` , `repo:${ this . owner }/${ this . repo }` , `is:public` , ... this . labels . map ( label => `label:${ label }` ) , ] . join ( ' ' ) , timestamp : Date . now ( ) , } ; const { data } = await this . $http . get < ResponseSearch < ResponseIssue > > ( `search/issues` , options ) ; const issue = data . items . map ( normalizeIssue ) . find ( item => item . title === issueTitle ) ; return issue || null ; } } see https async postIssue ( { accessToken , title , content , } ) { const { data } = await this . $http . post < ResponseIssue > ( `repos/${ this . owner }/${ this . repo }/issues` , { title , body : content , labels : this . labels , } , { headers : { Authorization : `token ${ accessToken }` } , } ) ; return normalizeIssue ( data ) ; } see https see https remarks async getComments ( { accessToken , issueId , query : { page = 1 , perPage = 10 } = { } , } ) { const issueOptions = { params : { timestamp : Date . now ( ) , } , } ; const commentsOptions = { params : { page : page , per_page : perPage , /**
                 * github v3 api does not support sort for issue comments
                 * have sent feedback to github support
                 */ timestamp : Date . now ( ) , } , headers : { Accept : [ 'application/vnd.github.v3.raw+json' , 'application/vnd.github.v3.html+json' , 'application/vnd.github.squirrel-girl-preview' , ] , } , } ; if ( accessToken ) { issueOptions . headers = { Authorization : `token ${ accessToken }` , } ; commentsOptions . headers . Authorization = `token ${ accessToken }` ; } const [ issueRes , commentsRes ] = await Promise . all ( [ this . $http . get < ResponseIssue > ( `repos/${ this . owner }/${ this . repo }/issues/${ issueId }` , issueOptions ) , this . $http . get < ResponseComment [ ] > ( `repos/${ this . owner }/${ this . repo }/issues/${ issueId }/comments` , commentsOptions ) , ] ) ; const linkHeader = commentsRes . headers . link || null ; const thisPage = /rel="next"/ . test ( linkHeader ) ? Number ( linkHeader . replace ( /^.*[^_]page=(\d*).*rel="next".*$/ , '$1' ) ) - 1 : /rel="prev"/ . test ( linkHeader ) ? Number ( linkHeader . replace ( /^.*[^_]page=(\d*).*rel="prev".*$/ , '$1' ) ) + 1 : 1 ; const thisPerPage = linkHeader ? Number ( linkHeader . replace ( /^.*per_page=(\d*).*$/ , '$1' ) ) : perPage ; return { count : Number ( issueRes . data . comments ) , page : thisPage , perPage : thisPerPage , data : commentsRes . data . map ( normalizeComment ) , } ; } see https async postComment ( { accessToken , issueId , content , } ) { const { data } = await this . $http . post < ResponseComment > ( `repos/${ this . owner }/${ this . repo }/issues/${ issueId }/comments` , { body : content , } , { headers : { Authorization : `token ${ accessToken }` , Accept : [ 'application/vnd.github.v3.raw+json' , 'application/vnd.github.v3.html+json' , 'application/vnd.github.squirrel-girl-preview' , ] , } , } ) ; return normalizeComment ( data ) ; } see https async putComment ( { accessToken , commentId , content , } ) { const { data } = await this . $http . patch < ResponseComment > ( `repos/${ this . owner }/${ this . repo }/issues/comments/${ commentId }` , { body : content , } , { headers : { Authorization : `token ${ accessToken }` , Accept : [ 'application/vnd.github.v3.raw+json' , 'application/vnd.github.v3.html+json' , 'application/vnd.github.squirrel-girl-preview' , ] , } , } ) ; return normalizeComment ( data ) ; } see https async deleteComment ( { accessToken , commentId , } ) { const { status } = await this . $http . delete ( `repos/${ this . owner }/${ this . repo }/issues/comments/${ commentId }` , { headers : { Authorization : `token ${ accessToken }` } , } ) ; return status === 204 ; } see https see https remarks async getCommentReactions ( { accessToken , commentId , } ) { const { data } = await this . $http . get < ResponseComment > ( `repos/${ this . owner }/${ this . repo }/issues/comments/${ commentId }` , { params : { timestamp : Date . now ( ) , } , headers : { Authorization : `token ${ accessToken }` , Accept : 'application/vnd.github.squirrel-girl-preview' , } , } ) ; return normalizeReactions ( data . reactions ) ; } see https async postCommentReaction ( { accessToken , commentId , reaction , } ) { const response = await this . $http . post < ResponseReaction > ( `repos/${ this . owner }/${ this . repo }/issues/comments/${ commentId }/reactions` , { content : mapReactionName ( reaction ) , } , { headers : { Authorization : `token ${ accessToken }` , Accept : 'application/vnd.github.squirrel-girl-preview' , } , } ) ; if ( response . status === 200 ) { return this . deleteCommentReaction ( { accessToken , commentId , reactionId : response . data . id , } ) ; } return response . status === 201 ; } see https async deleteCommentReaction ( { accessToken , commentId , reactionId , } ) { const response = await this . $http . delete ( `repos/${ this . owner }/${ this . repo }/issues/comments/${ commentId }/reactions/${ reactionId }` , { headers : { Authorization : `token ${ accessToken }` , Accept : 'application/vnd.github.squirrel-girl-preview' , } , } ) ; return response . status === 204 ; } }