UNPKG

chenv

Version:

cli to manage Chrome Web Store item

231 lines (222 loc) 5.94 kB
'use strict' Object.defineProperty(exports, '__esModule', { value: true }) function _interopDefault(ex) { return ex && typeof ex === 'object' && 'default' in ex ? ex['default'] : ex } var got = _interopDefault(require('got')) var dtz = _interopDefault(require('dtz')) var Zip = _interopDefault(require('jszip')) const toBody = ({ body }) => typeof body === 'object' ? body : JSON.parse(body) const throws = message => { throw new Error(message) } const asserts = (condition, message) => { if (!condition) throws(message) return } const joinParams = params => { const entries = Object.entries(params) .filter( ([key, value]) => Boolean(value) || typeof value === 'boolean' || typeof value === 'number' ) .map(([key, value]) => { const str = value return `${key}=${str}` }) return entries.length ? '?' + entries.join('&') : '' } const oauth2_uri = 'https://accounts.google.com/o/oauth2' const auth_uri = `${oauth2_uri}/auth` const token_uri = `${oauth2_uri}/token` const redirect_uri = 'urn:ietf:wg:oauth:2.0:oob' const authURL = client_id => { asserts(client_id, `[chenv] client_id is required`) return ( auth_uri + joinParams({ client_id, redirect_uri, response_type: 'code', scope: 'https://www.googleapis.com/auth/chromewebstore' }) ) } const getRefreshToken = ({ client_id, client_secret, code } = {}) => { asserts(client_id, `[chenv] client_id is required`) asserts(client_secret, `[chenv] client_secret is required`) asserts(code, `[chenv] code is required`) return got(token_uri, { method: 'POST', json: true, body: { client_id, client_secret, code, redirect_uri, grant_type: 'authorization_code' } }) .then(toBody) .then(({ refresh_token }) => refresh_token) } const getAccessToken = ({ client_id, client_secret, refresh_token } = {}) => { asserts(client_id, `[chenv] client_id is required`) asserts(client_secret, `[chenv] client_secret is required`) asserts(refresh_token, `[chenv] refresh_token is required`) return got(token_uri, { method: 'POST', json: true, body: { client_id, client_secret, refresh_token, redirect_uri, grant_type: 'refresh_token' } }) .then(toBody) .then(({ access_token }) => access_token) } const base_uri = `https://www.googleapis.com` const insert_uri = `${base_uri}/upload/chromewebstore/v1.1/items` const update_uri = id => `${insert_uri}/${id}` const publish_uri = id => `${check_uri(id)}/publish` const check_uri = (id, projection) => `${base_uri}/chromewebstore/v1.1/items/${id}${joinParams({ projection })}` const headers = access_token => { asserts(access_token, `[chenv] access_token is invalid`) return { 'x-goog-api-version': '2', Authorization: `Bearer ${access_token}` } } const insertItem = ({ token, body } = {}) => { asserts(body, `[chenv] body is required`) return got(insert_uri, { method: 'POST', headers: headers(token), body }).then(requestHandler) } const updateItem = ({ token, id, body } = {}) => { asserts(id, `[chenv] id is ${id}`) asserts(body, `[chenv] body is required`) return got(update_uri(id), { method: 'PUT', headers: headers(token), body }).then(requestHandler) } const checkItem = ({ token, id, projection } = {}) => { asserts(id, `[chenv] id is ${id}`) return got(check_uri(id, projection), { method: 'GET', headers: headers(token) }).then(requestHandler) } const requestHandler = res => { const body = toBody(res) asserts( body.uploadState === 'SUCCESS' || body.uploadState === 'IN_PROGRESS', JSON.stringify(body) ) return body } const publishItem = ({ token, id, target } = {}) => { asserts(id, `[chenv] id is ${id}`) asserts(target, `[chenv] target is ${target}`) return got(publish_uri(id), { method: 'POST', headers: headers(token), json: true, body: { target } }).then(toBody) } const manifestMap = { ['remove']: { manifest_version: 2, name: '(removed)', version: '0' }, ['create']: { manifest_version: 2, name: '', version: '0.0.0' } } const zipApp = async src => { asserts(src, `[chenv] src is ${src}`) const zip = await dtz(src) return zip.generateNodeStream() } const zipEmpty = manifestJson => { const zip = new Zip() zip.file('manifest.json', JSON.stringify(manifestJson)) return zip.generateNodeStream() } class Chenv { constructor({ client_id, client_secret, refresh_token } = {}) { asserts(client_id, `client_id is required`) asserts(client_secret, `client_secret is required`) asserts(refresh_token, `refresh_token is required`) this.token = '' this.credentials = { client_id, client_secret, refresh_token } } setToken() { return this.token ? false : getAccessToken(this.credentials).then(token => (this.token = token)) } async insertItem(src) { await this.setToken() return insertItem({ token: this.token, body: await zipApp(src) }) } async updateItem(id, src) { await this.setToken() return updateItem({ token: this.token, body: await zipApp(src), id }) } async publishItem(id, trustedTesters) { await this.setToken() return publishItem({ token: this.token, target: trustedTesters ? 'trustedTesters' : 'default', id }) } async removeItem(id) { await this.setToken() return updateItem({ token: this.token, body: zipEmpty(manifestMap['remove']), id }) } } exports.default = Chenv exports.authURL = authURL exports.getRefreshToken = getRefreshToken exports.getAccessToken = getAccessToken exports.insertItem = insertItem exports.updateItem = updateItem exports.checkItem = checkItem exports.publishItem = publishItem exports.Chenv = Chenv