aboutsummaryrefslogtreecommitdiffhomepage
path: root/application/components/github/api.js
blob: 97032e652b9f7a443d1dd0889f79c59aa4a8d7db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import EventEmitter from 'eventemitter3'
const axios = require('axios')

import * as config from '../../config'

export class API extends EventEmitter {
  token = null
  initialized = false
  installations = null
  repositories = null
  repoInstallationMap = null

  async _request (options) {
    if (typeof options === 'string') {
      options = {
        url: options
      }
    }

    if (options.url.startsWith('/')) {
      options.url = `${config.apiBaseUrl}${options.url}`
    }
  
    options.headers = Object.assign({}, options.headers)
    if (this.token && !options.headers.Authorization) {
      options.headers.Authorization = `Bearer ${this.token}`
    }
    
    try {
      return await axios(options)
    } catch (err) {
      if (err.response?.status === 401) {
        console.error('Authentication failed.')
        this.emit('authentication-failed', err.response)
      }

      throw err
    }
  }

  async init() {
    if (this.initialized) {
      return
    }

    const installationUrl = `${config.apiBaseUrl}/github/installation`
    const param = new URLSearchParams(location.search).get('token')
    if (!localStorage.auth_token && param) {
      history.replaceState({}, null, location.pathname)
      localStorage.auth_token = param
    }

    if (localStorage.auth_token) {
      this.token = localStorage.auth_token
      const { data } = await this._request(installationUrl)
      this.emit('authenticated')

      if (!data.installation) {
        console.warn('No GitHub app installation found for authenticated user.')
        this.emit('app-not-installed')
      }

      this.installations = data.installations
      this.repositories = data.repositories
      this.repoInstallationMap = data.repoInstallationMap
    }
  }

  beginLoginFlow() {
    localStorage.removeItem('auth_token')
    location.href = `${config.apiBaseUrl}/github/authorize`
  }

  beginInstallAppFlow() {
    location.href = `https://github.com/apps/${config.githubAppName}/installations/new`
  }
  
  isGitHubAuthorized() {
    return !!this.token
  }

  isAppInstalled() {
    return this.installations?.length && this.repositories?.length
  }

  async fetchRepoBranches(repo) {
    const installation = encodeURIComponent(this.repoInstallationMap[repo.full_name])
    const repository = encodeURIComponent(repo.full_name)
    const { data } = await this._request(
      `/github/installation/${installation}/${repository}/branches`
    )

    return data
  }

  async fetchLayoutAndKeymap(repo, branch) {
    const installation = encodeURIComponent(this.repoInstallationMap[repo])
    const repository = encodeURIComponent(repo)
    const url = new URL(`${config.apiBaseUrl}/github/keyboard-files/${installation}/${repository}`)

    if (branch) {
      url.search = new URLSearchParams({ branch }).toString()
    }

    try {
      const { data } = await this._request(url.toString())
      const defaultLayout = data.info.layouts.default || data.info.layouts[Object.keys(data.info.layouts)[0]]
      return {
        layout: defaultLayout.layout,
        keymap: data.keymap
      }
    } catch (err) {
      if (err.response?.status === 400) {
        console.error('Failed to load keymap and layout from github', err.response.data)
        this.emit('repo-validation-error', err.response.data)
      }

      throw err
    }
  }

  commitChanges(repo, branch, layout, keymap) {
    const installation = encodeURIComponent(this.repoInstallationMap[repo])
    const repository = encodeURIComponent(repo)

    return this._request({
      url: `/github/keyboard-files/${installation}/${repository}/${encodeURIComponent(branch)}`,
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      data: { layout, keymap }
    })
  }
}

export default new API()