From d0413dc4f703c359adc1c5e766ad2ca709d0e16f Mon Sep 17 00:00:00 2001 From: leonhartX Date: Sat, 8 Feb 2020 17:22:12 +0900 Subject: [PATCH 1/4] fix access token in query parameter --- manifest.json | 2 +- src/gas-api.js | 336 +++++++++++----------- src/gas-hub.js | 523 ++++++++++++++++++---------------- src/scm/bitbucket.js | 426 +++++++++++++++------------- src/scm/github.js | 656 +++++++++++++++++++++++-------------------- src/scm/gitlab.js | 267 ++++++++++-------- src/util.js | 14 + 7 files changed, 1181 insertions(+), 1043 deletions(-) diff --git a/manifest.json b/manifest.json index 5fcfdfe..beb8a10 100644 --- a/manifest.json +++ b/manifest.json @@ -1,5 +1,5 @@ { - "version": "4.0.6", + "version": "4.0.7", "manifest_version": 2, "default_locale": "en", "name": "__MSG_appName__", diff --git a/src/gas-api.js b/src/gas-api.js index ede3d8b..cbef787 100644 --- a/src/gas-api.js +++ b/src/gas-api.js @@ -4,42 +4,42 @@ class Gas { pull(code) { const changed = $('.diff-file:checked').toArray().map(e => e.value); const updatePromises = changed.filter(f => code.scm[f]) - .map((file) => { - const regex = new RegExp(`(.*?)(${context.config.filetype}|\.json|\.html)$`) - const match = file.match(regex); - if (!match || !match[1] || !match[2]) { - showAlert('Unsupported file type.', LEVEL_ERROR); - return; - } - if (match[2] === 'json' && match[1] !== 'appsscript') { - showAlert('Unsupported file type', LEVEL_ERROR); - return; - } - const name = match[1]; - const type = match[2]; + .map((file) => { + const regex = new RegExp(`(.*?)(${context.config.filetype}|\.json|\.html)$`) + const match = file.match(regex); + if (!match || !match[1] || !match[2]) { + showAlert('Unsupported file type.', LEVEL_ERROR); + return; + } + if (match[2] === 'json' && match[1] !== 'appsscript') { + showAlert('Unsupported file type', LEVEL_ERROR); + return; + } + const name = match[1]; + const type = match[2]; - if (!code.gas[file]) { - return () => this.gasCreateFile(name, type) - .then(() => { - return this.gasUpdateFile(name, code.scm[file]); - }) - } else { - return () => this.gasUpdateFile(name, code.scm[file]); - } - }) - .filter(n => n != undefined); + if (!code.gas[file]) { + return () => this.gasCreateFile(name, type) + .then(() => { + return this.gasUpdateFile(name, code.scm[file]); + }) + } else { + return () => this.gasUpdateFile(name, code.scm[file]); + } + }) + .filter(n => n != undefined); const deletePromises = changed.filter(f => !code.scm[f]) - .map((file) => { - const regex = new RegExp(`(.*?)(${context.config.filetype}|\.html)$`) - const match = file.match(regex); - if (!match || !match[1] || !match[2]) { - showAlert('Unknow Error', LEVEL_ERROR); - return; - } - const name = match[1]; - return () => this.gasDeleteFile(name); - }); + .map((file) => { + const regex = new RegExp(`(.*?)(${context.config.filetype}|\.html)$`) + const match = file.match(regex); + if (!match || !match[1] || !match[2]) { + showAlert('Unknow Error', LEVEL_ERROR); + return; + } + const name = match[1]; + return () => this.gasDeleteFile(name); + }); if (updatePromises.length === 0 && deletePromises.length === 0) { showAlert('Nothing to do', LEVEL_WARN); @@ -47,69 +47,109 @@ class Gas { } this.getGasContext() - .then(() => { - return Promise.all(updatePromises.map(f => f())) .then(() => { - return Promise.all(deletePromises.map(f => f())); + return Promise.all(updatePromises.map(f => f())) + .then(() => { + return Promise.all(deletePromises.map(f => f())); + }) }) - }) - .then(() => { - showAlert('Successfully pulled from scm'); - location.reload(); - }) - .catch((err) => { - showAlert(err.message, LEVEL_ERROR); - }); + .then(() => { + showAlert('Successfully pulled from scm'); + location.reload(); + }) + .catch((err) => { + showAlert(err.message, LEVEL_ERROR); + }); } /* - * get project context with google rpc - * this is very volatile since it is just inferred from code - */ + * get project context with google rpc + * this is very volatile since it is just inferred from code + */ getGasContext() { return new Promise((resolve, reject) => { - chrome.storage.local.get(['requestUrl' ,'requestHeaders', 'requestBody', 'gasToken'], resolve); - }) - .then((param) => { - context.gasUrl = param.requestUrl; - context.gasHeaders = param.requestHeaders; - context.gasToken = param.gasToken; - return param.requestBody; - }); + chrome.storage.local.get(['requestUrl', 'requestHeaders', 'requestBody', 'gasToken'], resolve); + }) + .then((param) => { + context.gasUrl = param.requestUrl; + context.gasHeaders = param.requestHeaders; + context.gasToken = param.gasToken; + return param.requestBody; + }); } getGasCode() { return this.getGasContext() - .then((requestBody) => { - return $.ajax({ - url: context.gasUrl, - headers: context.gasHeaders, - method: 'POST', - crossDomain: true, - data: requestBody, - dataType: 'text' + .then((requestBody) => { + return $.ajax({ + url: context.gasUrl, + headers: context.gasHeaders, + method: 'POST', + crossDomain: true, + data: requestBody, + dataType: 'text' + }) }) - }) - .then((response) => { - if (!response.startsWith('//OK')) throw new Error('Init failed'); - //evil eval, but it's simple to get the object since it's not valid json object - const initData = eval(response.slice(4)).filter((e) => { - return typeof(e) === 'object'; - })[0]; - const ids = initData.filter((data) => { return /^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/.test(data) }); - context.projectId = initData[initData.indexOf(ids[0]) + 2]; - if (context.projectId === context.id) { - // for bounded script - context.id = initData[initData.indexOf(ids[0]) + 1]; - } + .then((response) => { + if (!response.startsWith('//OK')) throw new Error('Init failed'); + //evil eval, but it's simple to get the object since it's not valid json object + const initData = eval(response.slice(4)).filter((e) => { + return typeof (e) === 'object'; + })[0]; + const ids = initData.filter((data) => { + return /^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/.test(data) + }); + context.projectId = initData[initData.indexOf(ids[0]) + 2]; + if (context.projectId === context.id) { + // for bounded script + context.id = initData[initData.indexOf(ids[0]) + 1]; + } + + if (context.projectId.length != 33) { + reject(new Error('cant not get project ID')); + } + const promises = ids.map((id) => { + return new Promise((resolve, reject) => { + const payload = `7|1|9|${getBaseUrl()}\|${context.gasToken}|_|getFileContent|l|${id}|${context.id}|${context.projectId}|m|1|2|3|4|1|5|5|6|7|8|9|0|0|`; + $.ajax({ + url: context.gasUrl, + headers: context.gasHeaders, + method: 'POST', + crossDomain: true, + data: payload, + dataType: 'text' + }) + .then((response) => { + if (!response.startsWith('//OK')) reject(new Error('get apps script code failed')); + //evil eval, but it's simple to get the object since it's not valid json object + const codeContent = eval(response.slice(4)).filter((e) => { + return typeof (e) === 'object'; + })[0]; + resolve({ + file: codeContent[codeContent.length - 7], + content: codeContent[codeContent.length - 10], + id: id + }); + }) + .fail(reject); + }) + }); + return Promise.all(promises); + }) + .then((responses) => { + context.fileIds = responses.reduce((hash, elem) => { + if (elem) hash[elem.file] = elem.id; + return hash; + }, {}); + return responses; + }) + } - if (context.projectId.length != 33) { - reject(new Error('cant not get project ID')); - } - const promises = ids.map((id) => { - return new Promise((resolve, reject) => { - const payload = `7|1|9|${getBaseUrl()}\|${context.gasToken}|_|getFileContent|l|${id}|${context.id}|${context.projectId}|m|1|2|3|4|1|5|5|6|7|8|9|0|0|`; - $.ajax({ + gasCreateFile(file, type) { + const typeId = type === context.config.filetype ? 0 : 2; + const payload = `7|1|7|${getBaseUrl()}\|${context.gasToken}|_|makeNewFile|1a|i|${file}|1|2|3|4|2|5|6|7|6|${typeId}|`; + return new Promise((resolve, reject) => { + $.ajax({ url: context.gasUrl, headers: context.gasHeaders, method: 'POST', @@ -117,102 +157,76 @@ class Gas { data: payload, dataType: 'text' }) - .then((response) => { - if (!response.startsWith('//OK')) reject(new Error('get apps script code failed')); - //evil eval, but it's simple to get the object since it's not valid json object - const codeContent = eval(response.slice(4)).filter((e) => { - return typeof(e) === 'object'; - })[0]; - resolve({file : codeContent[codeContent.length - 7], content: codeContent[codeContent.length - 10], id : id }); + .then(resolve) + .fail((err) => { + reject(new Error('Create file failed')) }) - .fail(reject); - }) - }); - return Promise.all(promises); - }) - .then((responses) => { - context.fileIds = responses.reduce((hash, elem) => { - if (elem) hash[elem.file] = elem.id; - return hash; - }, {}); - return responses; - }) - } - - gasCreateFile(file, type) { - const typeId = type === context.config.filetype ? 0 : 2; - const payload = `7|1|7|${getBaseUrl()}\|${context.gasToken}|_|makeNewFile|1a|i|${file}|1|2|3|4|2|5|6|7|6|${typeId}|`; - return new Promise((resolve, reject) => { - $.ajax({ - url: context.gasUrl, - headers: context.gasHeaders, - method: 'POST', - crossDomain: true, - data: payload, - dataType: 'text' }) - .then(resolve) - .fail((err) => {reject(new Error('Create file failed'))}) - }) - .then((response) => { - if (!response.startsWith('//OK')) throw(new Error(`Create file '${file}.${type}' failed`)); - const responseData = eval(response.slice(4)).filter((e) => { - return typeof(e) === 'object'; - })[0]; - const id = responseData.filter((data) => { return /^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/.test(data) }); - if (id.length > 0) { - context.fileIds[file] = id[0]; - return id[0]; - } else { - throw new Error('can not parse response'); - } - }); + .then((response) => { + if (!response.startsWith('//OK')) throw (new Error(`Create file '${file}.${type}' failed`)); + const responseData = eval(response.slice(4)).filter((e) => { + return typeof (e) === 'object'; + })[0]; + const id = responseData.filter((data) => { + return /^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/.test(data) + }); + if (id.length > 0) { + context.fileIds[file] = id[0]; + return id[0]; + } else { + throw new Error('can not parse response'); + } + }); } gasUpdateFile(file, code) { const escapedCode = code.replace(/\\/g, '\\\\').replace(/\|/g, '\\!'); const payload = `7|1|7|${getBaseUrl()}\|${context.gasToken}|_|updateFile|1a|${file}|${escapedCode}|1|2|3|4|2|5|5|6|7|`; let headers = context.gasHeaders; - Object.assign(headers, { 'file-id': context.fileIds[file]}); + Object.assign(headers, { + 'file-id': context.fileIds[file] + }); return new Promise((resolve, reject) => { $.ajax({ - url: context.gasUrl, - headers: headers, - method: 'POST', - crossDomain: true, - data: payload, - dataType: 'text' - }) - .then((response) => { - if (!response.startsWith('//OK')) reject(new Error('Update file failed')); - resolve(); - }) - .fail((err) => { - reject(new Error('Update file failed')); - }); + url: context.gasUrl, + headers: headers, + method: 'POST', + crossDomain: true, + data: payload, + dataType: 'text' + }) + .then((response) => { + if (!response.startsWith('//OK')) reject(new Error('Update file failed')); + resolve(); + }) + .fail((err) => { + reject(new Error('Update file failed')); + }); }); } gasDeleteFile(file) { const payload = `7|1|4|${getBaseUrl()}\|${context.gasToken}|_|deleteFile|1|2|3|4|0|`; let headers = context.gasHeaders; - Object.assign(headers, { 'file-id': context.fileIds[file]}); + Object.assign(headers, { + 'file-id': context.fileIds[file] + }); return new Promise((resolve, reject) => { $.ajax({ - url: context.gasUrl, - headers: headers, - method: 'POST', - crossDomain: true, - data: payload, - dataType: 'text' - }) - .then((response) => { - if (!response.startsWith('//OK')) reject(new Error('Delete file failed')); - resolve(); - }) - .fail((err) => { - reject(new Error('Update file failed')); - }); + url: context.gasUrl, + headers: headers, + method: 'POST', + crossDomain: true, + data: payload, + dataType: 'text' + }) + .then((response) => { + if (!response.startsWith('//OK')) reject(new Error('Delete file failed')); + resolve(); + }) + .fail((err) => { + reject(new Error('Update file failed')); + }); }); } } \ No newline at end of file diff --git a/src/gas-hub.js b/src/gas-hub.js index 41792ba..8c47736 100644 --- a/src/gas-hub.js +++ b/src/gas-hub.js @@ -13,29 +13,29 @@ const observer = new MutationObserver((e) => { $(() => { initPageContent() - .then(initContext) - .then(updateRepo) - .then(updateBranch) - .then(updateGist) - .then(initPageEvent) - .catch((err) => { - switch (err.message) { - case 'need login' : - initLoginContent(); - break; - case 'not match' : - break; - case 'nothing' : - break; - case 'need relogin': - initLoginContent(); - showAlert('Extension has been updated, please relogin', LEVEL_WARN); - break; - default: - showAlert('Unknow Error', LEVEL_ERROR); - break; - } - }); + .then(initContext) + .then(updateRepo) + .then(updateBranch) + .then(updateGist) + .then(initPageEvent) + .catch((err) => { + switch (err.message) { + case 'need login': + initLoginContent(); + break; + case 'not match': + break; + case 'nothing': + break; + case 'need relogin': + initLoginContent(); + showAlert('Extension has been updated, please relogin', LEVEL_WARN); + break; + default: + showAlert(err, LEVEL_ERROR); + break; + } + }); }); function initContext() { @@ -46,86 +46,90 @@ function initContext() { context.id = match[2]; return new Promise((resolve, reject) => { - chrome.storage.sync.get([ - 'scm', - 'token', - 'user', - 'baseUrl', - 'bindRepo', - 'bindBranch', - 'bindType', - 'bindPattern', - 'bindConfig' - ], (item) => { - if (!item.token) { - reject(new Error('need login')); - } - scm = createSCM(item); - context.bindRepo = item.bindRepo || {}; - context.bindBranch = item.bindBranch || {}; - context.bindType = item.bindType || {}; - context.bindPattern = item.bindPattern || {}; - context.bindConfig = item.bindConfig || {}; - context.config = context.bindConfig[context.id] || {}; - context.config.filetype = context.config.filetype || context.bindType[context.id] || '.gs'; - context.config.ignorePattern = context.config.ignorePattern || context.bindPattern[context.id] || []; - context.config.manifestEnabled = context.config.manifestEnabled || false; - context.gist = context.bindRepo[context.id] && context.bindRepo[context.id].gist; - resolve(scm); - }); - }) - .then(scm => { - return scm.getNamespaces() - .then((owners) => { - owners.forEach((owner) => { - let content = `` - $('#new-repo-owner').append(content); + chrome.storage.sync.get([ + 'scm', + 'token', + 'user', + 'baseUrl', + 'bindRepo', + 'bindBranch', + 'bindType', + 'bindPattern', + 'bindConfig' + ], (item) => { + if (!item.token) { + reject(new Error('need login')); + } + scm = createSCM(item); + context.bindRepo = item.bindRepo || {}; + context.bindBranch = item.bindBranch || {}; + context.bindType = item.bindType || {}; + context.bindPattern = item.bindPattern || {}; + context.bindConfig = item.bindConfig || {}; + context.config = context.bindConfig[context.id] || {}; + context.config.filetype = context.config.filetype || context.bindType[context.id] || '.gs'; + context.config.ignorePattern = context.config.ignorePattern || context.bindPattern[context.id] || []; + context.config.manifestEnabled = context.config.manifestEnabled || false; + context.gist = context.bindRepo[context.id] && context.bindRepo[context.id].gist; + resolve(scm); }); - return scm; }) - }) - .then(scm => { - return scm.getRepos(); - }) + .then(scm => { + return scm.getNamespaces() + .then((owners) => { + owners.forEach((owner) => { + let content = `` + $('#new-repo-owner').append(content); + }); + return scm; + }) + }) + .then(scm => { + return scm.getRepos(); + }) } function initPageContent() { return Promise.all([ - $.get(chrome.runtime.getURL('content/button.html')), - $.get(chrome.runtime.getURL('content/menu.html')), - $.get(chrome.runtime.getURL('content/modal.html')) - ]) - .then((content) => { - $('#functionSelect').after(content[0]); - $('body').children().last().after(content[1]); - $('body').children().last().after(content[2]); - }) - .then(() => { - $(document).on('click', '.scm-alert-dismiss', () => { - $('.scm-alert').remove(); + $.get(chrome.runtime.getURL('content/button.html')), + $.get(chrome.runtime.getURL('content/menu.html')), + $.get(chrome.runtime.getURL('content/modal.html')) + ]) + .then((content) => { + $('#functionSelect').after(content[0]); + $('body').children().last().after(content[1]); + $('body').children().last().after(content[2]); + }) + .then(() => { + $(document).on('click', '.scm-alert-dismiss', () => { + $('.scm-alert').remove(); + }); + chrome.runtime.sendMessage({ + cmd: 'tab' + }); }); - chrome.runtime.sendMessage({ cmd: 'tab' }); - }); } function initLoginContent() { $.get(chrome.runtime.getURL('content/login.html')) - .then((content) => { - $('#functionSelect').after(content); - $('#login').hover(() => { - $('#login').addClass('goog-toolbar-menu-button-hover'); - }, () => { - $('#login').removeClass('goog-toolbar-menu-button-hover'); - }); - $('#login').click(() => { - if (chrome.runtime.openOptionsPage) { - chrome.runtime.openOptionsPage(); - } else { - window.open(chrome.runtime.getURL('options/options.html')); - } + .then((content) => { + $('#functionSelect').after(content); + $('#login').hover(() => { + $('#login').addClass('goog-toolbar-menu-button-hover'); + }, () => { + $('#login').removeClass('goog-toolbar-menu-button-hover'); + }); + $('#login').click(() => { + if (chrome.runtime.openOptionsPage) { + chrome.runtime.openOptionsPage(); + } else { + window.open(chrome.runtime.getURL('options/options.html')); + } + }); + chrome.runtime.sendMessage({ + cmd: 'tab' + }); }); - chrome.runtime.sendMessage({ cmd: 'tab' }); - }); } function initPageEvent() { @@ -134,10 +138,10 @@ function initPageEvent() { ['repo', 'branch'].forEach((type) => { const container = $(`.${type}-menu`); const button = $(`#${type}Select`); - if (!container.is(event.target) - && !button.is(event.target) - && container.has(event.target).length === 0 - && button.has(event.target).length == 0) { + if (!container.is(event.target) && + !button.is(event.target) && + container.has(event.target).length === 0 && + button.has(event.target).length == 0) { container.hide(); $(`#${type}Select`).removeClass('goog-toolbar-menu-button-open'); } @@ -182,10 +186,10 @@ function initPageEvent() { $(document).on('click', `#scm-create-${type}`, () => { changeModalState(type, false); scm[`create${type.capitalize()}`]() - .then(window[`handle${type.capitalize()}Created`]) - .catch(err => { - showAlert(err.message, LEVEL_ERROR); - }) + .then(window[`handle${type.capitalize()}Created`]) + .catch(err => { + showAlert(err.message, LEVEL_ERROR); + }) }); $(document).on('input propertychange', `#new-${type}-name`, (event) => { @@ -211,8 +215,12 @@ function initPageEvent() { ['pull', 'push'].forEach(type => { $(document).on('click', `#${type}-button`, () => { prepareCode() - .then((data) => { showDiff(data, type); }) //get more performance over callback - .catch((err) => { showAlert(err.message, LEVEL_ERROR); }); + .then((data) => { + showDiff(data, type); + }) //get more performance over callback + .catch((err) => { + showAlert(err.message, LEVEL_ERROR); + }); }); }) @@ -225,11 +233,13 @@ function initPageEvent() { $(document).on('click', '#save-config', () => { context.config.filetype = $('#filetype').val(); - context.config.manifestEnabled = $('#manage-manifest').prop( "checked" ); + context.config.manifestEnabled = $('#manage-manifest').prop("checked"); context.config.ignorePattern = $('#ignore-pattern').val().split(';').filter(p => p !== ''); context.bindConfig[context.id] = context.config; try { - chrome.storage.sync.set({ bindConfig: context.bindConfig }); + chrome.storage.sync.set({ + bindConfig: context.bindConfig + }); changeModalState('config', false); } catch (err) { showAlert(err.message, LEVEL_ERROR); @@ -245,18 +255,18 @@ function initPageEvent() { let content; let label; switch (type) { - case 'repo' : + case 'repo': if (context.repo && target.text() === context.repo.fullName) return; //update context.repo with name and fullName const fullName = target.attr('data'); content = { - fullName : fullName, + fullName: fullName, gist: fullName === 'gist' } label = fullName; context.gist = content.gist; break; - case 'branch' : + case 'branch': if (context[type] && target.text() === context[type]) return; content = target.attr('data'); label = target.attr('data'); @@ -266,8 +276,12 @@ function initPageEvent() { } context[type] = content; const bindName = `bind${type.capitalize()}`; - Object.assign(context[bindName], { [context.id] : content }); - chrome.storage.sync.set({ [bindName]: context[bindName] }, () => { + Object.assign(context[bindName], { + [context.id]: content + }); + chrome.storage.sync.set({ + [bindName]: context[bindName] + }, () => { $(`#${type}Select`).removeClass('goog-toolbar-menu-button-open'); $(`.${type}-menu`).hide(); if (type === 'repo') { @@ -286,31 +300,31 @@ function initPageEvent() { function prepareCode() { return Promise.all([gas.getGasCode(), scm.getCode()]) - .then((data) => { - const re = new RegExp(`\\${context.config.filetype}$`); - const files = $('.item > .gwt-Label').toArray().reduce((hash, e) => { - if (context.config.manifestEnabled && e.title === 'appsscript.json') { - hash['appsscript'] = 'appsscript.json'; - } - const match = e.title.match(/(.*?)\.(gs|html)$/); - if (!match || !match[1] || !match[2]) return hash; - hash[match[1]] = match[0]; - return hash; - }, {}); - const code = { - gas: data[0].reduce((hash, elem) => { - if (elem && files[elem.file]) hash[files[elem.file].replace(/\.gs$/, context.config.filetype)] = elem.content; - return hash; - }, {}), - scm: data[1].reduce((hash, elem) => { - if (elem) { - hash[elem.file] = elem.content; + .then((data) => { + const re = new RegExp(`\\${context.config.filetype}$`); + const files = $('.item > .gwt-Label').toArray().reduce((hash, e) => { + if (context.config.manifestEnabled && e.title === 'appsscript.json') { + hash['appsscript'] = 'appsscript.json'; } + const match = e.title.match(/(.*?)\.(gs|html)$/); + if (!match || !match[1] || !match[2]) return hash; + hash[match[1]] = match[0]; return hash; - }, {}) - } - return code; - }) + }, {}); + const code = { + gas: data[0].reduce((hash, elem) => { + if (elem && files[elem.file]) hash[files[elem.file].replace(/\.gs$/, context.config.filetype)] = elem.content; + return hash; + }, {}), + scm: data[1].reduce((hash, elem) => { + if (elem) { + hash[elem.file] = elem.content; + } + return hash; + }, {}) + } + return code; + }) } function showDiff(code, type) { @@ -324,49 +338,54 @@ function showDiff(code, type) { const gasFiles = Object.keys(code.gas); const scmFiles = Object.keys(code.scm); let diff = scmFiles.filter((e) => { - return gasFiles.indexOf(e) < 0; - }) - .concat(gasFiles) - .filter(file => { - if (context.config.manifestEnabled && file === 'appsscript.json') { - return true; - } - for (let i = 0; i < context.config.ignorePattern.length; i ++) { - let p = new RegExp(context.config.ignorePattern[i]); - if (p.test(file)) return false; - } - const regex = new RegExp(`(.*?)(${context.config.filetype}|\.html)$`) - const match = file.match(regex); - return match && match[1] && match[2]; - }) - .reduce((diff, file) => { - let mode = null; - if (!oldCode[file]) { - mode = 'new file mode 100644'; - } else if (!newCode[file]) { - if (file === 'appsscript.json') { - return diff; //can not delete manifest file + return gasFiles.indexOf(e) < 0; + }) + .concat(gasFiles) + .filter(file => { + if (context.config.manifestEnabled && file === 'appsscript.json') { + return true; } - mode = 'deleted file mode 100644'; - } - let fileDiff = JsDiff.createPatch(file, oldCode[file] || '', newCode[file] || ''); - if (fileDiff.indexOf('@@') < 0) return diff; //no diff - let diffArr = fileDiff.split('\n'); - diffArr.splice(0, 2, `diff --git a/${file} b/${file}`); - if (mode) { - diffArr.splice(1, 0, mode); - } - fileDiff = diffArr.join('\n'); - return diff + fileDiff; - }, ''); + for (let i = 0; i < context.config.ignorePattern.length; i++) { + let p = new RegExp(context.config.ignorePattern[i]); + if (p.test(file)) return false; + } + const regex = new RegExp(`(.*?)(${context.config.filetype}|\.html)$`) + const match = file.match(regex); + return match && match[1] && match[2]; + }) + .reduce((diff, file) => { + let mode = null; + if (!oldCode[file]) { + mode = 'new file mode 100644'; + } else if (!newCode[file]) { + if (file === 'appsscript.json') { + return diff; //can not delete manifest file + } + mode = 'deleted file mode 100644'; + } + let fileDiff = JsDiff.createPatch(file, oldCode[file] || '', newCode[file] || ''); + if (fileDiff.indexOf('@@') < 0) return diff; //no diff + let diffArr = fileDiff.split('\n'); + diffArr.splice(0, 2, `diff --git a/${file} b/${file}`); + if (mode) { + diffArr.splice(1, 0, mode); + } + fileDiff = diffArr.join('\n'); + return diff + fileDiff; + }, ''); if (diff === '') { showAlert('Everything already up-to-date', LEVEL_WARN); return; } - const diffHtml = new Diff2HtmlUI({diff : diff}); - diffHtml.draw('.scm-diff', {inputFormat: 'json', showFiles: false}); + const diffHtml = new Diff2HtmlUI({ + diff: diff + }); + diffHtml.draw('.scm-diff', { + inputFormat: 'json', + showFiles: false + }); diffHtml.highlightCode('.scm-diff'); $('.d2h-file-name-wrapper').each((i, e) => { const filename = $(e).children('.d2h-file-name').text(); @@ -410,24 +429,24 @@ function updateRepo(repos) { if (scm.canUseGist) { $('.repo-menu').append('
gist
'); } - + repos.forEach((repo) => { let content = `
${repo}
` $('.repo-menu').append(content); }); if (context.repo) { $('#scm-bind-repo').text(`Repo: ${context.repo.fullName}`); - + //highlight current repo in repos list let repoItems = document.getElementsByClassName('scm-item goog-menuitem'); for (let i = 0; i < repoItems.length; i++) { let currentItem = repoItems[i]; if (context.repo.fullName === currentItem.innerText) { - currentItem.style.background = "lightgrey"; - break; + currentItem.style.background = "lightgrey"; + break; } } - + return context.repo.fullName; } return null; @@ -438,24 +457,28 @@ function updateGist() { return null; } return scm.getAllGists() - .then((gists) => { - $('.branch-menu').empty().append('
Create new gist
'); - gists.forEach((gist) => { - let tooltip = gist.description === '' ? 'no description' : gist.description; - let content = `
${gist.id}
` - $('.branch-menu').append(content); + .then((gists) => { + $('.branch-menu').empty().append('
Create new gist
'); + gists.forEach((gist) => { + let tooltip = gist.description === '' ? 'no description' : gist.description; + let content = `
${gist.id}
` + $('.branch-menu').append(content); + }); + let gist = context.bindBranch[context.id]; + if ($.inArray(gist, gists.map(gist => gist.id)) < 0) { + gist = ''; + } + $('#scm-bind-branch').text(`Gist: ${gist}`); + //update context and storage + context.branch = gist; + Object.assign(context.bindBranch, { + [context.id]: gist + }); + chrome.storage.sync.set({ + bindBranch: context.bindBranch + }); + return gist; }); - let gist = context.bindBranch[context.id]; - if ($.inArray(gist, gists.map(gist => gist.id)) < 0) { - gist = ''; - } - $('#scm-bind-branch').text(`Gist: ${gist}`); - //update context and storage - context.branch = gist; - Object.assign(context.bindBranch, { [context.id] : gist }); - chrome.storage.sync.set({ bindBranch: context.bindBranch }); - return gist; - }); } function updateBranch() { @@ -463,68 +486,72 @@ function updateBranch() { return null; } return scm.getAllBranches() - .then((branches) => { - $('.branch-menu').empty().append('
Create new branch
'); - branches.forEach((branch) => { - let content = `
${branch.name}
` - $('.branch-menu').append(content); - }); - let branch = context.bindBranch[context.id]; - if (branches.length === 0) { - branch = ''; - if (scm.name === 'github') { - showAlert('This repository is empty, try to create a new branch such as [master] in Github', LEVEL_WARN); - } else { - showAlert('This repository is empty, first create a new branch', LEVEL_WARN); + .then((branches) => { + $('.branch-menu').empty().append('
Create new branch
'); + branches.forEach((branch) => { + let content = `
${branch.name}
` + $('.branch-menu').append(content); + }); + let branch = context.bindBranch[context.id]; + if (branches.length === 0) { + branch = ''; + if (scm.name === 'github') { + showAlert('This repository is empty, try to create a new branch such as [master] in Github', LEVEL_WARN); + } else { + showAlert('This repository is empty, first create a new branch', LEVEL_WARN); + } + } else if ($.inArray(branch, branches.map(branch => branch.name)) < 0) { + branch = ($.inArray("master", branches.map(branch => branch.name)) >= 0) ? 'master' : branches[0].name; } - } else if ($.inArray(branch, branches.map(branch => branch.name)) < 0) { - branch = ($.inArray("master", branches.map(branch => branch.name)) >= 0) ? 'master' : branches[0].name; - } - $('#scm-bind-branch').text(`Branch: ${branch}`); - //update context and storage - context.branch = branch; - Object.assign(context.bindBranch, { [context.id] : branch }); - chrome.storage.sync.set({ bindBranch: context.bindBranch }); - return branch; - }); + $('#scm-bind-branch').text(`Branch: ${branch}`); + //update context and storage + context.branch = branch; + Object.assign(context.bindBranch, { + [context.id]: branch + }); + chrome.storage.sync.set({ + bindBranch: context.bindBranch + }); + return branch; + }); } function handleRepoCreated(repo) { return scm.getRepos() - .then(updateRepo) - .then(updateBranch) - .then(() => { - $('#new-repo-name').val(''); - $('#new-repo-desc').val(''); - $('#new-repo-type').val('public'); - showAlert(`Successfully create new repository ${repo}`); - }) - .catch(() => { - throw new Error('Repository created, but failed to show the new repository.'); - }); + .then(updateRepo) + .then(updateBranch) + .then(() => { + $('#new-repo-name').val(''); + $('#new-repo-desc').val(''); + $('#new-repo-type').val('public'); + showAlert(`Successfully create new repository ${repo}`); + }) + .catch(() => { + throw new Error('Repository created, but failed to show the new repository.'); + }); } function handleBranchCreated(branch) { return updateBranch() - .then(() => { - $('#new-branch-name').val(''); - showAlert(`Successfully create new branch: ${branch}`); - }) - .catch(() => { - throw new Error('Branch created, but failed to show the new branch.'); - }); + .then(() => { + $('#new-branch-name').val(''); + showAlert(`Successfully create new branch: ${branch}`); + }) + .catch(() => { + throw new Error('Branch created, but failed to show the new branch.'); + }); } function handleGistCreated() { return updateGist() - .then(() => { - $('#new-gist-name').val(''); - $('#new-gist-public').val('public'); - showAlert(`Successfully create new gist.`); - }) - .catch(err => { - throw new Error('Gist created, but failed to show the new gist.'); - }); + .then(() => { + $('#new-gist-name').val(''); + $('#new-gist-public').val('public'); + showAlert(`Successfully create new gist.`); + }) + .catch(err => { + throw new Error('Gist created, but failed to show the new gist.'); + }); } function getBaseUrl() { @@ -560,15 +587,17 @@ function changeButtonState(type, value) { * level: info, warning, error * but the class is promo. info, warning */ -function showAlert(message, level=LEVEL_INFO) { +function showAlert(message, level = LEVEL_INFO) { $.get(chrome.runtime.getURL('content/alert.html')) - .then((content) => { - observer.disconnect(); - $('#docs-butterbar-container').empty().append(content.replace(/_LEVEL_/g, level).replace(/_MESSAGE_/, message)); - observer.observe(document.getElementById('docs-butterbar-container'), { childList: true }); - }) + .then((content) => { + observer.disconnect(); + $('#docs-butterbar-container').empty().append(content.replace(/_LEVEL_/g, level).replace(/_MESSAGE_/, message)); + observer.observe(document.getElementById('docs-butterbar-container'), { + childList: true + }); + }) } -String.prototype.capitalize = function() { - return this.charAt(0).toUpperCase() + this.slice(1); -} +String.prototype.capitalize = function () { + return this.charAt(0).toUpperCase() + this.slice(1); +} \ No newline at end of file diff --git a/src/scm/bitbucket.js b/src/scm/bitbucket.js index 9e2d643..695218f 100644 --- a/src/scm/bitbucket.js +++ b/src/scm/bitbucket.js @@ -15,34 +15,36 @@ class Bitbucket { get canUseGist() { return false; - } + } getAccessToken() { return new Promise((resolve, reject) => { - $.ajax({ - url: 'https://bitbucket.org/site/oauth2/access_token', - headers: { - Authorization: `Basic RmZIVE02ZnN5NDJQQlJDRjRQOmVDZDN0TTh5TUpUeTJSMld4bTJWUzZoYWVKdnpuNzdw` - }, - method: 'POST', - dataType: 'json', - contentType: 'application/x-www-form-urlencoded', - data: { - grant_type: 'refresh_token', - refresh_token: this.token - } + $.ajax({ + url: 'https://bitbucket.org/site/oauth2/access_token', + headers: { + Authorization: `Basic RmZIVE02ZnN5NDJQQlJDRjRQOmVDZDN0TTh5TUpUeTJSMld4bTJWUzZoYWVKdnpuNzdw` + }, + method: 'POST', + dataType: 'json', + contentType: 'application/x-www-form-urlencoded', + data: { + grant_type: 'refresh_token', + refresh_token: this.token + } + }) + .then(resolve) + .fail(reject) + }) + .then(response => { + chrome.storage.sync.set({ + token: response.refresh_token + }); + this.accessToken = response.access_token; + return response.access_token; + }) + .catch(err => { + showAlert(`Failed to refresh access token: ${err}`, LEVEL_ERROR); }) - .then(resolve) - .fail(reject) - }) - .then(response => { - chrome.storage.sync.set({ token: response.refresh_token }); - this.accessToken = response.access_token; - return response.access_token; - }) - .catch(err => { - showAlert('Failed to refresh access token.', LEVEL_ERROR); - }) } commitFiles(repo, branch, parent, files, deleteFiles, comment) { @@ -62,131 +64,136 @@ class Bitbucket { data.parents = parent; } $.ajax({ - url: `${this.baseUrl}/repositories/${repo}/src`, - headers: { - 'Authorization': `Bearer ${this.accessToken}` - }, - contentType: 'application/x-www-form-urlencoded', - method: 'POST', - crossDomain: true, - traditional: true, - data: data, - }) - .then(resolve) - .fail(reject); + url: `${this.baseUrl}/repositories/${repo}/src`, + headers: { + 'Authorization': `Bearer ${this.accessToken}` + }, + contentType: 'application/x-www-form-urlencoded', + method: 'POST', + crossDomain: true, + traditional: true, + data: data, + }) + .then(resolve) + .fail(reject); }); } - push(code){ + push(code) { const changed = $('.diff-file:checked').toArray().map(elem => elem.value); const files = changed.filter(f => code.gas[f]).map(f => { - return { name: f, content: code.gas[f] } + return { + name: f, + content: code.gas[f] + } }); const deleteFiles = changed.filter(f => !code.gas[f]); const comment = $('#commit-comment').val(); this.commitFiles(context.repo.fullName, context.branch, null, files, deleteFiles, comment) - .then(() => { - showAlert(`Successfully push to ${context.branch} of ${context.repo.fullName}`); - }) - .catch((err) => { - showAlert('Failed to push', LEVEL_ERROR); - }); + .then(() => { + showAlert(`Successfully push to ${context.branch} of ${context.repo.fullName}`); + }) + .catch((err) => { + showAlert('Failed to push', LEVEL_ERROR); + }); } getAllBranches() { return this.getAccessToken() - .then(accessToken => { - return getAllItems(Promise.resolve( - { - token: accessToken, - items: [], - url: `${this.baseUrl}/repositories/${context.repo.fullName}/refs/branches?access_token=${accessToken}` - }), - this.followPaginate, - 'bitbucket' - ); - }); + .then(accessToken => { + return getAllItems(Promise.resolve({ + token: accessToken, + items: [], + url: `${this.baseUrl}/repositories/${context.repo.fullName}/refs/branches?access_token=${accessToken}` + }), + this.followPaginate, + 'bitbucket' + ); + }); } getCode() { return this.getAccessToken() - .then(accessToken => { - return $.getJSON( - `${this.baseUrl}/repositories/${context.repo.fullName}/refs/branches/${context.branch}`, - { access_token: accessToken } - ) - }) - .then(response => { - return getAllItems(Promise.resolve( - { - token: this.accessToken, - items: [], - urls: [], - url: `${this.baseUrl}/repositories/${context.repo.fullName}/src/${response.target.hash}/?access_token=${this.accessToken}` - }), - this.followDirectory, - 'bitbucket' - ) + .then(accessToken => { + return $.getJSON( + `${this.baseUrl}/repositories/${context.repo.fullName}/refs/branches/${context.branch}`, { + access_token: accessToken + } + ) + }) .then(response => { - const promises = response.map(src => { - return new Promise((resolve, reject) => { - $.get(src.links.self.href, { access_token: this.accessToken }) - .then(content => { - resolve({ file: src.path, content: content}); - }) - .fail(reject) + return getAllItems(Promise.resolve({ + token: this.accessToken, + items: [], + urls: [], + url: `${this.baseUrl}/repositories/${context.repo.fullName}/src/${response.target.hash}/?access_token=${this.accessToken}` + }), + this.followDirectory, + 'bitbucket' + ) + .then(response => { + const promises = response.map(src => { + return new Promise((resolve, reject) => { + $.get(src.links.self.href, { + access_token: this.accessToken + }) + .then(content => { + resolve({ + file: src.path, + content: content + }); + }) + .fail(reject) + }); + }); + return Promise.all(promises); }); - }); - return Promise.all(promises); }); - }); } getNamespaces() { return this.getAccessToken() - .then(accessToken => { - return getAllItems(Promise.resolve( - { - token: accessToken, - items: [], - url: `${this.baseUrl}/teams?access_token=${accessToken}&role=contributor` - }), - this.followPaginate, - 'bitbucket' - ); - }) - .then(teams => { - this.namespaces = [this.user].concat(teams.map(team => team.username)); - return this.namespaces; - }) - .catch((err) => { - showAlert('Failed to get user info.', LEVEL_ERROR); - }); + .then(accessToken => { + return getAllItems(Promise.resolve({ + token: accessToken, + items: [], + url: `${this.baseUrl}/teams?access_token=${accessToken}&role=contributor` + }), + this.followPaginate, + 'bitbucket' + ); + }) + .then(teams => { + this.namespaces = [this.user].concat(teams.map(team => team.username)); + return this.namespaces; + }) + .catch((err) => { + showAlert('Failed to get user info.', LEVEL_ERROR); + }); } getRepos() { return this.getAccessToken() - .then(accessToken => { - return getAllItems(Promise.resolve( - { - token: accessToken, - items: [], - url: `${this.baseUrl}/repositories/?access_token=${accessToken}&q=scm="git"&role=contributor` - }), - this.followPaginate, - 'bitbucket' - ); - }) - .then(response => { - const repos = response.map(repo => repo.full_name); - //if current bind still existed, use it - const repo = context.bindRepo[context.id]; - if (repo && $.inArray(repo.fullName, repos) >= 0) { - context.repo = repo; - } - return repos; - }); + .then(accessToken => { + return getAllItems(Promise.resolve({ + token: accessToken, + items: [], + url: `${this.baseUrl}/repositories/?access_token=${accessToken}&q=scm="git"&role=contributor` + }), + this.followPaginate, + 'bitbucket' + ); + }) + .then(response => { + const repos = response.map(repo => repo.full_name); + //if current bind still existed, use it + const repo = context.bindRepo[context.id]; + if (repo && $.inArray(repo.fullName, repos) >= 0) { + context.repo = repo; + } + return repos; + }); } createRepo() { @@ -196,111 +203,126 @@ class Bitbucket { const isPrivate = $('#new-repo-type').val() !== 'public'; const payload = { scm: 'git', - description : desc, + description: desc, is_private: isPrivate } if (!name || name === '') return; return this.getAccessToken() - .then(() => { - return $.ajax({ - url: `${this.baseUrl}/repositories/${owner}/${name}`, - headers: { - 'Authorization': `Bearer ${this.accessToken}` - }, - method: 'POST', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) - }) - }) - .then(response => { - const repo = { - fullName : response.full_name - }; - context.repo = repo; - Object.assign(context.bindRepo, { [context.id] : repo }); - if (context.bindBranch[context.id]) { - delete context.bindBranch[context.id]; - } - chrome.storage.sync.set({ bindRepo: context.bindRepo }); - return response.full_name; - }) - .then(repo => { - return this.commitFiles(repo, 'master', null, [{name: "README.md", content: "initialed by gas-github"}], null, 'initial commit') .then(() => { - return repo; + return $.ajax({ + url: `${this.baseUrl}/repositories/${owner}/${name}`, + headers: { + 'Authorization': `Bearer ${this.accessToken}` + }, + method: 'POST', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }) + }) + .then(response => { + const repo = { + fullName: response.full_name + }; + context.repo = repo; + Object.assign(context.bindRepo, { + [context.id]: repo + }); + if (context.bindBranch[context.id]) { + delete context.bindBranch[context.id]; + } + chrome.storage.sync.set({ + bindRepo: context.bindRepo + }); + return response.full_name; + }) + .then(repo => { + return this.commitFiles(repo, 'master', null, [{ + name: "README.md", + content: "initialed by gas-github" + }], null, 'initial commit') + .then(() => { + return repo; + }); + }) + .catch((err) => { + throw new Error('Failed to create new repository.'); }); - }) - .catch((err) => { - throw new Error('Failed to create new repository.'); - }); } createBranch() { const branch = $('#new-branch-name').val(); if (!branch || branch === '') return; return this.getAccessToken() - .then(() => { - return $.getJSON( - `${this.baseUrl}/repositories/${context.repo.fullName}/refs/branches/${context.branch}`, - { access_token: this.accessToken } - ); - }) - .then(res => { - const parent = res.target? res.target.hash : null; - return this.commitFiles(context.repo.fullName, branch, parent, [], null, `create new branch ${branch}`); - }) - .then(() => { - context.branch = branch; - Object.assign(context.bindBranch, { [context.id] : branch }); - chrome.storage.sync.set({ bindBranch: context.bindBranch }); - return branch; - }) - .catch(err => { - throw new Error('Failed to create new branch.'); - }); + .then(() => { + return $.getJSON( + `${this.baseUrl}/repositories/${context.repo.fullName}/refs/branches/${context.branch}`, { + access_token: this.accessToken + } + ); + }) + .then(res => { + const parent = res.target ? res.target.hash : null; + return this.commitFiles(context.repo.fullName, branch, parent, [], null, `create new branch ${branch}`); + }) + .then(() => { + context.branch = branch; + Object.assign(context.bindBranch, { + [context.id]: branch + }); + chrome.storage.sync.set({ + bindBranch: context.bindBranch + }); + return branch; + }) + .catch(err => { + throw new Error('Failed to create new branch.'); + }); } followPaginate(data) { return new Promise((resolve, reject) => { $.getJSON(data.url) - .then(response => { - data.items = data.items.concat(response.values); - const link = response.next; - let url = null; - if (link) { - url = link; - } - resolve({ items: data.items, url: url }); - }) - .fail(reject); + .then(response => { + data.items = data.items.concat(response.values); + const link = response.next; + let url = null; + if (link) { + url = link; + } + resolve({ + items: data.items, + url: url + }); + }) + .fail(reject); }) } followDirectory(data) { return new Promise((resolve, reject) => { $.getJSON(data.url) - .then(response => { - const dirs = response.values.filter(src => { - return src.type === 'commit_directory'; - }).map(dir => { - return `${dir.links.self.href}?access_token=${data.token}`; + .then(response => { + const dirs = response.values.filter(src => { + return src.type === 'commit_directory'; + }).map(dir => { + return `${dir.links.self.href}?access_token=${data.token}`; + }) + const re = new RegExp(`(\\${context.config.filetype}|\\.html${context.config.manifestEnabled ? '|^appsscript.json' : ''})$`); + const files = response.values.filter(src => { + return src.type === 'commit_file' && re.test(src.path); + }); + data.items = data.items.concat(files); + data.urls = data.urls.concat(dirs); + let link = response.next; + if (link) { + data.urls.push(`${link}&access_token=${data.token}`); + } + data.url = data.urls.shift(); + resolve(data); }) - const re = new RegExp(`(\\${context.config.filetype}|\\.html${context.config.manifestEnabled ? '|^appsscript.json' : ''})$`); - const files = response.values.filter(src => { - return src.type === 'commit_file' && re.test(src.path); - }); - data.items = data.items.concat(files); - data.urls = data.urls.concat(dirs); - let link = response.next; - if (link) { - data.urls.push(`${link}&access_token=${data.token}`); - } - data.url = data.urls.shift(); - resolve(data); - }) - .fail(reject); + .fail(reject); }) } -} +} \ No newline at end of file diff --git a/src/scm/github.js b/src/scm/github.js index 63bacc2..7142a4f 100644 --- a/src/scm/github.js +++ b/src/scm/github.js @@ -16,7 +16,8 @@ class Github { return true; } - push(code){ + + push(code) { if (context.gist) return this.pushToGist(code); return this.pushToRepo(code); } @@ -29,19 +30,22 @@ class Github { encoding: 'utf-8' }; return $.ajax({ - url: `${this.baseUrl}/repos/${context.repo.fullName}/git/blobs`, - headers: { - 'Authorization': `token ${this.accessToken}` - }, - method: 'POST', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) - }) - .then(response => { - return {file: file, blob: response}; - }) + url: `${this.baseUrl}/repos/${context.repo.fullName}/git/blobs`, + headers: { + 'Authorization': `token ${this.accessToken}` + }, + method: 'POST', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }) + .then(response => { + return { + file: file, + blob: response + }; + }) }); if (changed.length === 0) { showAlert('Nothing to do', LEVEL_WARN); @@ -49,39 +53,66 @@ class Github { } Promise.all([ - Promise.all(promises), - $.getJSON( - `${this.baseUrl}/repos/${context.repo.fullName}/branches/${context.branch}`, - { access_token: this.accessToken } - ) - ]) - .then(responses => { - return $.getJSON( - responses[1].commit.commit.tree.url, - { - recursive: 1, - access_token: this.accessToken - } - ) - .then(baseTree => { - const tree = responses[0].map((data) => { - return { - path: data.file, - mode: '100644', - type: 'blob', - sha: data.blob.sha - } - }) - .concat(baseTree.tree.filter((t) => { - return (t.type != 'tree') && (changed.indexOf(t.path) < 0); - })); - return { - tree: tree - }; + Promise.all(promises), + getGitHubJSON( + `${this.baseUrl}/repos/${context.repo.fullName}/branches/${context.branch}`, + this.accessToken) + ]) + .then(responses => { + return getGitHubJSON( + responses[1].commit.commit.tree.url, + this.accessToken, { + recursive: 1 + } + ) + .then(baseTree => { + const tree = responses[0].map((data) => { + return { + path: data.file, + mode: '100644', + type: 'blob', + sha: data.blob.sha + } + }) + .concat(baseTree.tree.filter((t) => { + return (t.type != 'tree') && (changed.indexOf(t.path) < 0); + })); + return { + tree: tree + }; + }) + .then(payload => { + return $.ajax({ + url: `${this.baseUrl}/repos/${context.repo.fullName}/git/trees`, + headers: { + 'Authorization': `token ${this.accessToken}` + }, + method: 'POST', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }); + }) + .then(response => { + return Object.assign(response, { + parent: responses[1].commit.sha + }) + }) + .fail(err => { + throw err; + }); }) - .then(payload => { + .then(response => { + const payload = { + message: $('#commit-comment').val(), + tree: response.sha, + parents: [ + response.parent + ] + }; return $.ajax({ - url: `${this.baseUrl}/repos/${context.repo.fullName}/git/trees`, + url: `${this.baseUrl}/repos/${context.repo.fullName}/git/commits`, headers: { 'Authorization': `token ${this.accessToken}` }, @@ -93,55 +124,28 @@ class Github { }); }) .then(response => { - return Object.assign(response, { parent: responses[1].commit.sha }) + const payload = { + force: true, + sha: response.sha + }; + return $.ajax({ + url: `${this.baseUrl}/repos/${context.repo.fullName}/git/refs/heads/${context.branch}`, + headers: { + 'Authorization': `token ${this.accessToken}` + }, + method: 'PATCH', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }); }) - .fail(err => { - throw err; - }); - }) - .then(response => { - const payload = { - message: $('#commit-comment').val(), - tree: response.sha, - parents: [ - response.parent - ] - }; - return $.ajax({ - url: `${this.baseUrl}/repos/${context.repo.fullName}/git/commits`, - headers: { - 'Authorization': `token ${this.accessToken}` - }, - method: 'POST', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) - }); - }) - .then(response => { - const payload = { - force: true, - sha: response.sha - }; - return $.ajax({ - url: `${this.baseUrl}/repos/${context.repo.fullName}/git/refs/heads/${context.branch}`, - headers: { - 'Authorization': `token ${this.accessToken}` - }, - method: 'PATCH', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) + .then(() => { + showAlert(`Successfully push to ${context.branch} of ${context.repo.fullName}`); + }) + .catch(err => { + showAlert('Failed to push', LEVEL_ERROR); }); - }) - .then(() => { - showAlert(`Successfully push to ${context.branch} of ${context.repo.fullName}`); - }) - .catch(err => { - showAlert('Failed to push', LEVEL_ERROR); - }); } pushToGist(code) { @@ -165,27 +169,31 @@ class Github { payload.description = $('#gist-desc').val(); } return $.ajax({ - url: `${this.baseUrl}/gists/${context.branch}`, - headers: { - 'Authorization': `token ${this.accessToken}` - }, - method: 'PATCH', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) - }) - .then(() => { - showAlert(`Successfully update gist: ${context.branch}`); - }) - .fail(err => { - showAlert('Failed to update', LEVEL_ERROR); - }); + url: `${this.baseUrl}/gists/${context.branch}`, + headers: { + 'Authorization': `token ${this.accessToken}` + }, + method: 'PATCH', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }) + .then(() => { + showAlert(`Successfully update gist: ${context.branch}`); + }) + .fail(err => { + showAlert(`Failed to update: ${err}`, LEVEL_ERROR); + }); } getAllGists() { return getAllItems( - Promise.resolve({ items: [], url: `${this.baseUrl}/users/${this.user}/gists?access_token=${this.accessToken}` }), + Promise.resolve({ + accessToken: this.accessToken, + items: [], + url: `${this.baseUrl}/users/${this.user}/gists` + }), this.followPaginate, 'github' ); @@ -193,7 +201,11 @@ class Github { getAllBranches() { return getAllItems( - Promise.resolve({ items: [], url: `${this.baseUrl}/repos/${context.repo.fullName}/branches?access_token=${this.accessToken}` }), + Promise.resolve({ + accessToken: this.accessToken, + items: [], + url: `${this.baseUrl}/repos/${context.repo.fullName}/branches` + }), this.followPaginate, 'github' ); @@ -206,104 +218,115 @@ class Github { getRepoCode() { return new Promise((resolve, reject) => { - $.getJSON( - `${this.baseUrl}/repos/${context.repo.fullName}/branches/${context.branch}`, - { access_token: this.accessToken } - ) - .then(resolve) - .fail(reject); - }) - .then(response => { - return $.getJSON( - `${this.baseUrl}/repos/${context.repo.fullName}/git/trees/${response.commit.commit.tree.sha}`, - { recursive: 1, access_token: this.accessToken } - ); - }) - .then(response => { - const re = new RegExp(`(\\${context.config.filetype}|\\.html${context.config.manifestEnabled ? '|^appsscript.json' : ''})$`); - const promises = response.tree.filter((tree) => { - return tree.type === 'blob' && re.test(tree.path); + getGitHubJSON( + `${this.baseUrl}/repos/${context.repo.fullName}/branches/${context.branch}`, + this.accessToken) + .then(resolve) + .fail(reject); + }) + .then(response => { + return getGitHubJSON( + `${this.baseUrl}/repos/${context.repo.fullName}/git/trees/${response.commit.commit.tree.sha}`, + this.accessToken, { + recursive: 1 + } + ); }) - .map(tree => { - return new Promise((resolve, reject) => { - $.getJSON(tree.url, { access_token: this.accessToken }) - .then((content) => { - resolve({ file: tree.path, content: decodeURIComponent(escape(atob(content.content))) }); + .then(response => { + const re = new RegExp(`(\\${context.config.filetype}|\\.html${context.config.manifestEnabled ? '|^appsscript.json' : ''})$`); + const promises = response.tree.filter((tree) => { + return tree.type === 'blob' && re.test(tree.path); }) - .fail(reject) - }); + .map(tree => { + return new Promise((resolve, reject) => { + getGitHubJSON(tree.url, this.accessToken) + .then((content) => { + resolve({ + file: tree.path, + content: decodeURIComponent(escape(atob(content.content))) + }); + }) + .fail(reject) + }); + }); + return Promise.all(promises); }); - return Promise.all(promises); - }); } getGistCode() { return new Promise((resolve, reject) => { - $.getJSON( - `${this.baseUrl}/gists/${context.branch}`, - { access_token: this.accessToken } - ) - .then(resolve) - .fail(reject); - }) - .then((response) => { - const promises = Object.keys(response.files).map((filename) => { - let file = response.files[filename]; - return new Promise((resolve, reject) => { - if (file.truncated) { - $.getJSON(file.raw_url, {access_token: this.accessToken }) - .then((content) => { - resolve({ file: filename, content: content}); - }) - .fail(reject) - } else { - resolve({file: filename, content: file.content}); - } + getGitHubJSON(`${this.baseUrl}/gists/${context.branch}`, this.accessToken) + .then(resolve) + .fail(reject); + }) + .then((response) => { + const promises = Object.keys(response.files).map((filename) => { + let file = response.files[filename]; + return new Promise((resolve, reject) => { + if (file.truncated) { + getGitHubJSON(file.raw_url, this.accessToken) + .then((content) => { + resolve({ + file: filename, + content: content + }); + }) + .fail(reject) + } else { + resolve({ + file: filename, + content: file.content + }); + } + }); }); + return Promise.all(promises); }); - return Promise.all(promises); - }); } getRepos() { return getAllItems( - Promise.resolve({ items: [], url: `${this.baseUrl}/user/repos?access_token=${this.accessToken}` }), - this.followPaginate, - 'github' - ) - .then(response => { - const repos = response.map(repo => repo.full_name); - //if current bind still existed, use it - const repo = context.bindRepo[context.id]; - if (repo && $.inArray(repo.fullName, repos) >= 0) { - context.repo = repo; - } else if (context.gist) { - context.repo = { - fullName: 'gist', - gist: true + Promise.resolve({ + accessToken: this.accessToken, + items: [], + url: `${this.baseUrl}/user/repos` + }), + this.followPaginate, + 'github' + ) + .then(response => { + const repos = response.map(repo => repo.full_name); + //if current bind still existed, use it + const repo = context.bindRepo[context.id]; + if (repo && $.inArray(repo.fullName, repos) >= 0) { + context.repo = repo; + } else if (context.gist) { + context.repo = { + fullName: 'gist', + gist: true + } } - } - return repos; - }); + return repos; + }); } getNamespaces() { - return getAllItems(Promise.resolve( - { - token: this.accessToken, - items: [], - url: `${this.baseUrl}/user/orgs?access_token=${this.accessToken}` - }), - this.followPaginate, - 'github' - ) - .then(orgs => { - this.namespaces = [this.user].concat(orgs.map(org => org.login)); - return this.namespaces; - }) - .catch((err) => { - showAlert('Failed to get user info.', LEVEL_ERROR); - }); + return getAllItems( + Promise.resolve({ + accessToken: this.accessToken, + items: [], + url: `${this.baseUrl}/user/orgs` + }), + this.followPaginate, + 'github' + ) + .then(orgs => { + this.namespaces = [this.user].concat(orgs.map(org => org.login)); + return this.namespaces; + }) + .catch((err) => { + showAlert('Failed to get user info.', LEVEL_ERROR); + }); } createRepo() { @@ -312,156 +335,169 @@ class Github { const desc = $('#new-repo-desc').val(); const isPrivate = $('#new-repo-type').val() !== 'public'; const payload = { - name : name, - description : desc, - auto_init : true, + name: name, + description: desc, + auto_init: true, private: isPrivate } const path = owner === this.user ? '/user/repos' : `/orgs/${owner}/repos`; if (!name || name === '') return; return new Promise((resolve, reject) => { - $.ajax({ - url: `${this.baseUrl}${path}`, - headers: { - 'Authorization': `token ${this.accessToken}` - }, - method: 'POST', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) + $.ajax({ + url: `${this.baseUrl}${path}`, + headers: { + 'Authorization': `token ${this.accessToken}` + }, + method: 'POST', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }) + .then(resolve) + .fail(reject); }) - .then(resolve) - .fail(reject); - }) - .then(response => { - const repo = { - fullName : response.full_name - }; - context.repo = repo; - Object.assign(context.bindRepo, { [context.id] : repo }); - if (context.bindBranch[context.id]) { - delete context.bindBranch[context.id]; - } - chrome.storage.sync.set({ bindRepo: context.bindRepo }); - return response.full_name; - }) - .catch(err => { - throw new Error('Failed to create new repository.'); - }); + .then(response => { + const repo = { + fullName: response.full_name + }; + context.repo = repo; + Object.assign(context.bindRepo, { + [context.id]: repo + }); + if (context.bindBranch[context.id]) { + delete context.bindBranch[context.id]; + } + chrome.storage.sync.set({ + bindRepo: context.bindRepo + }); + return response.full_name; + }) + .catch(err => { + throw new Error('Failed to create new repository.'); + }); } createGist() { const desc = $('#new-gist-name').val(); const isPublic = $('#new-gist-public').val() !== 'secret'; const payload = { - 'description' : desc, + 'description': desc, 'public': isPublic, 'files': { - 'init_by_gas_hub.html' : { + 'init_by_gas_hub.html': { 'content': 'init by gas-hub, just delete this file.' } } }; return new Promise((resolve, reject) => { - $.ajax({ - url: `${this.baseUrl}/gists`, - headers: { - 'Authorization': `token ${this.accessToken}` - }, - method: 'POST', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) + $.ajax({ + url: `${this.baseUrl}/gists`, + headers: { + 'Authorization': `token ${this.accessToken}` + }, + method: 'POST', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }) + .then(resolve) + .fail(reject); }) - .then(resolve) - .fail(reject); - }) - .then(response => { - const gist = response.id; - context.branch = gist; - Object.assign(context.bindBranch, { [context.id] : gist }); - chrome.storage.sync.set({ bindBranch: context.bindBranch }); - return response; - }) - .catch(err => { - throw new Error('Failed to create new gist.'); - }); + .then(response => { + const gist = response.id; + context.branch = gist; + Object.assign(context.bindBranch, { + [context.id]: gist + }); + chrome.storage.sync.set({ + bindBranch: context.bindBranch + }); + return response; + }) + .catch(err => { + throw new Error('Failed to create new gist.'); + }); } createBranch() { const branch = $('#new-branch-name').val(); if (!branch || branch === '') return; return new Promise((resolve, reject) => { - $.getJSON( - `${this.baseUrl}/repos/${context.repo.fullName}/git/refs/heads/${context.branch}`, - { access_token: this.accessToken } - ) - .then(resolve) - .fail(reject) - }) - .then(response => { - if (response.object) { - return response.object.sha; - } - else { - return $.getJSON( - `${this.baseUrl}/repos/${context.repo.fullName}/git/refs/heads`, - { access_token: this.accessToken } - ) - .then(response => { - return response[0].object.sha; - }) - } - }) - .then(sha => { - const payload = { - ref: `refs/heads/${branch}`, - sha: sha - }; - return $.ajax({ - url: `${this.baseUrl}/repos/${context.repo.fullName}/git/refs`, - headers: { - 'Authorization': `token ${this.accessToken}` - }, - method: 'POST', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) + getGitHubJSON( + `${this.baseUrl}/repos/${context.repo.fullName}/git/refs/heads/${context.branch}`, + this.accessToken) + .then(resolve) + .fail(reject) + }) + .then(response => { + if (response.object) { + return response.object.sha; + } else { + return getGitHubJSON( + `${this.baseUrl}/repos/${context.repo.fullName}/git/refs/heads`, + this.accessToken) + .then(response => { + return response[0].object.sha; + }) + } + }) + .then(sha => { + const payload = { + ref: `refs/heads/${branch}`, + sha: sha + }; + return $.ajax({ + url: `${this.baseUrl}/repos/${context.repo.fullName}/git/refs`, + headers: { + 'Authorization': `token ${this.accessToken}` + }, + method: 'POST', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }); + }) + .then(response => { + context.branch = branch; + Object.assign(context.bindBranch, { + [context.id]: branch + }); + chrome.storage.sync.set({ + bindBranch: context.bindBranch + }); + return branch; + }) + .catch(err => { + if (err.status === 409) { + throw new Error('Cannot create branch in empty repository with API, try to create branch in Github.'); + } else { + throw new Error('Failed to create new branch.'); + } }); - }) - .then(response => { - context.branch = branch; - Object.assign(context.bindBranch, { [context.id] : branch }); - chrome.storage.sync.set({ bindBranch: context.bindBranch }); - return branch; - }) - .catch(err => { - if (err.status === 409) { - throw new Error('Cannot create branch in empty repository with API, try to create branch in Github.'); - } else { - throw new Error('Failed to create new branch.'); - } - }); } - + followPaginate(data) { return new Promise((resolve, reject) => { - $.getJSON(data.url) - .then((response, status, xhr) => { - data.items = data.items.concat(response); - const link = xhr.getResponseHeader('Link'); - let url = null; - if (link) { - const match = link.match(/<(.*?)>; rel="next"/); - url = match && match[1] ? match[1] : null; - } - resolve({ items: data.items, url: url }); - }) - .fail(reject); + getGitHubJSON(data.url, data.accessToken) + .then((response, status, xhr) => { + data.items = data.items.concat(response); + const link = xhr.getResponseHeader('Link'); + let url = null; + if (link) { + const match = link.match(/.*<(.*?)>; rel="next"/); + url = match && match[1] ? match[1] : null; + } + resolve({ + items: data.items, + url: url, + accessToken: data.accessToken + }); + }) + .fail(reject); }); } } \ No newline at end of file diff --git a/src/scm/gitlab.js b/src/scm/gitlab.js index a549490..bb7eefe 100644 --- a/src/scm/gitlab.js +++ b/src/scm/gitlab.js @@ -9,7 +9,7 @@ class Gitlab { groups: {} }; this.accessToken = token.token; - if(token.type === 'oAuth') { + if (token.type === 'oAuth') { this.tokenParam = `access_token=${this.accessToken}`; this.tokenHeader = { 'Authorization': `Bearer ${this.accessToken}` @@ -33,7 +33,11 @@ class Gitlab { commitFiles(repo, branch, parent, newFiles, changedFiles, deleteFiles, comment) { return new Promise((resolve, reject) => { - const data = {branch: branch, commit_message: comment, actions: []}; + const data = { + branch: branch, + commit_message: comment, + actions: [] + }; if (newFiles && newFiles.length > 0) { data.actions = data.actions.concat(newFiles.map((file) => { return { @@ -53,29 +57,34 @@ class Gitlab { })); } if (deleteFiles && deleteFiles.length > 0) { - data.actions = data.actions.concat(deleteFiles.map((file) => {return { - action : 'delete', - file_path : file - }})); + data.actions = data.actions.concat(deleteFiles.map((file) => { + return { + action: 'delete', + file_path: file + } + })); } $.ajax({ - url: `${this.baseUrl}/projects/${context.repo.id}/repository/commits`, - headers: this.tokenHeader, - contentType: 'application/json', - method: 'POST', - crossDomain: true, - traditional: true, - data: JSON.stringify(data) - }) - .then(resolve) - .fail(reject); + url: `${this.baseUrl}/projects/${context.repo.id}/repository/commits`, + headers: this.tokenHeader, + contentType: 'application/json', + method: 'POST', + crossDomain: true, + traditional: true, + data: JSON.stringify(data) + }) + .then(resolve) + .fail(reject); }); } push(code) { const changed = $('.diff-file:checked').toArray().map(elem => elem.value); const changedFiles = changed.filter(f => code.gas[f]).map(f => { - return {name: f, content: code.gas[f]} + return { + name: f, + content: code.gas[f] + } }); const deleteFiles = changed.filter(f => !code.gas[f]); const newFileNames = changed.filter(f => !code.scm[f]); @@ -91,14 +100,13 @@ class Gitlab { showAlert(`Successfully push to ${context.branch} of ${context.repo.fullName}`); }) .catch((err) => { - showAlert('Failed to push', LEVEL_ERROR); + showAlert(`Failed to push: ${err}`, LEVEL_ERROR); }); } getAllBranches() { context.repo.id = context.repo.id || this.namesToIds.repos[context.repo.fullName]; - return getAllItems(Promise.resolve( - { + return getAllItems(Promise.resolve({ tokenParam: this.tokenParam, items: [], url: `${this.baseUrl}/projects/${context.repo.id}/repository/branches?per_page=25` @@ -110,70 +118,71 @@ class Gitlab { getCode() { return new Promise((resolve, reject) => { - return $.getJSON( - `${this.baseUrl}/projects/${context.repo.id}/repository/tree?ref=${context.branch}&recursive=true&${this.tokenParam}`, {} - ) - .then(resolve) - .fail(reject) - }) - .then(response => { - const re = new RegExp(`(\\${context.config.filetype}|\\.html${context.config.manifestEnabled ? '|^appsscript.json' : ''})$`); - const promises = response.filter((tree) => { - return tree.type === 'blob' && re.test(tree.path); + return $.getJSON( + `${this.baseUrl}/projects/${context.repo.id}/repository/tree?ref=${context.branch}&recursive=true&${this.tokenParam}`, {} + ) + .then(resolve) + .fail(reject) }) - .map(tree => { - return new Promise((resolve, reject) => { - $.getJSON(`${this.baseUrl}/projects/${context.repo.id}/repository/files/${encodeURIComponent(tree.path)}?ref=${context.branch}&${this.tokenParam}`, {}) - .then((content) => { - resolve({file: tree.path, content: decodeURIComponent(escape(atob(content.content)))}); + .then(response => { + const re = new RegExp(`(\\${context.config.filetype}|\\.html${context.config.manifestEnabled ? '|^appsscript.json' : ''})$`); + const promises = response.filter((tree) => { + return tree.type === 'blob' && re.test(tree.path); }) - .fail(reject) - }); + .map(tree => { + return new Promise((resolve, reject) => { + $.getJSON(`${this.baseUrl}/projects/${context.repo.id}/repository/files/${encodeURIComponent(tree.path)}?ref=${context.branch}&${this.tokenParam}`, {}) + .then((content) => { + resolve({ + file: tree.path, + content: decodeURIComponent(escape(atob(content.content))) + }); + }) + .fail(reject) + }); + }); + return Promise.all(promises); }); - return Promise.all(promises); - }); } getNamespaces() { - return getAllItems(Promise.resolve( - { - tokenParam: this.tokenParam, - items: [], - url: `${this.baseUrl}/groups?per_page=25` - }), - this.followPaginate, - 'gitlab' - ) - .then(groups => { - this.namespaces = [this.user].concat(groups.map(group => group.name)); - this.namesToIds.groups = groups.reduce((obj, item) => (obj[item.name] = item.id, obj), {}); - return this.namespaces; - }) - .catch((err) => { - showAlert('Failed to get user info.', LEVEL_ERROR); - }); + return getAllItems(Promise.resolve({ + tokenParam: this.tokenParam, + items: [], + url: `${this.baseUrl}/groups?per_page=25` + }), + this.followPaginate, + 'gitlab' + ) + .then(groups => { + this.namespaces = [this.user].concat(groups.map(group => group.name)); + this.namesToIds.groups = groups.reduce((obj, item) => (obj[item.name] = item.id, obj), {}); + return this.namespaces; + }) + .catch((err) => { + showAlert('Failed to get user info.', LEVEL_ERROR); + }); } getRepos() { // Named Projects in gitlab - return getAllItems(Promise.resolve( - { - tokenParam: this.tokenParam, - items: [], - url: `${this.baseUrl}/projects?per_page=25&membership=true` - }), - this.followPaginate, - 'gitlab' - ) - .then(response => { - this.namesToIds.repos = response.reduce((obj, item) => (obj[item.path_with_namespace] = item.id, obj), {}); - const repos = Object.keys(this.namesToIds.repos); - //if current bind still existed, use it - const repo = context.bindRepo[context.id]; - if (repo && $.inArray(repo.fullName, repos) >= 0) { - context.repo = repo; - } - return repos; - }); + return getAllItems(Promise.resolve({ + tokenParam: this.tokenParam, + items: [], + url: `${this.baseUrl}/projects?per_page=25&membership=true` + }), + this.followPaginate, + 'gitlab' + ) + .then(response => { + this.namesToIds.repos = response.reduce((obj, item) => (obj[item.path_with_namespace] = item.id, obj), {}); + const repos = Object.keys(this.namesToIds.repos); + //if current bind still existed, use it + const repo = context.bindRepo[context.id]; + if (repo && $.inArray(repo.fullName, repos) >= 0) { + context.repo = repo; + } + return repos; + }); } createRepo() { @@ -191,40 +200,47 @@ class Gitlab { } if (!name || name === '') return; return new Promise((resolve, reject) => { - return $.ajax({ - url: `${this.baseUrl}/projects`, - headers: this.tokenHeader, - method: 'POST', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) + return $.ajax({ + url: `${this.baseUrl}/projects`, + headers: this.tokenHeader, + method: 'POST', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }) + .then(resolve) + .fail(reject); }) - .then(resolve) - .fail(reject); - }) - .then(response => { - const repo = { - fullName: response.path_with_namespace, - id: response.id - }; - context.repo = repo; - Object.assign(context.bindRepo, {[context.id]: repo}); - if (context.bindBranch[context.id]) { - delete context.bindBranch[context.id]; - } - chrome.storage.sync.set({bindRepo: context.bindRepo}); - return response.path_with_namespace; - }) - .then(repo => { - return this.commitFiles(repo, 'master', null, [{name: "README.md", content: "initialed by gas-github"}], null,null, 'initial commit') - .then(() => { - return repo; + .then(response => { + const repo = { + fullName: response.path_with_namespace, + id: response.id + }; + context.repo = repo; + Object.assign(context.bindRepo, { + [context.id]: repo + }); + if (context.bindBranch[context.id]) { + delete context.bindBranch[context.id]; + } + chrome.storage.sync.set({ + bindRepo: context.bindRepo }); + return response.path_with_namespace; }) - .catch((err) => { - throw new Error('Failed to create new repository.'); - }); + .then(repo => { + return this.commitFiles(repo, 'master', null, [{ + name: "README.md", + content: "initialed by gas-github" + }], null, null, 'initial commit') + .then(() => { + return repo; + }); + }) + .catch((err) => { + throw new Error(`Failed to create new repository: ${err}`); + }); } createBranch() { @@ -236,23 +252,27 @@ class Gitlab { if (!branch || branch === '') return; return new Promise((resolve, reject) => { return $.ajax({ - url: `${this.baseUrl}/projects/${context.repo.id}/repository/branches`, - headers: this.tokenHeader, - method: 'POST', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) - }) + url: `${this.baseUrl}/projects/${context.repo.id}/repository/branches`, + headers: this.tokenHeader, + method: 'POST', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }) .then(resolve) .fail(reject); }) - .then(response => { - context.branch = branch; - Object.assign(context.bindBranch, { [context.id] : branch }); - chrome.storage.sync.set({ bindBranch: context.bindBranch }); - return branch; - }) + .then(response => { + context.branch = branch; + Object.assign(context.bindBranch, { + [context.id]: branch + }); + chrome.storage.sync.set({ + bindBranch: context.bindBranch + }); + return branch; + }) .catch(err => { throw new Error('Failed to create new branch.'); }); @@ -269,9 +289,12 @@ class Gitlab { const match = link.match(/<([^ ]*?)>; rel="next"/); url = match && match[1] ? match[1] : null; } - resolve({ items: data.items, url: url }); + resolve({ + items: data.items, + url: url + }); }) .fail(reject); }) } -} +} \ No newline at end of file diff --git a/src/util.js b/src/util.js index 76b6758..4f61a66 100644 --- a/src/util.js +++ b/src/util.js @@ -18,4 +18,18 @@ function createSCM(item) { default: return new Github(item.baseUrl, item.user, item.token); } +} + +function getGitHubJSON(url, accessToken, data) { + return $.ajax({ + url: url, + headers: { + 'Authorization': `token ${accessToken}` + }, + method: 'GET', + crossDomain: true, + dataType: 'json', + data: data, + contentType: 'application/json' + }) } \ No newline at end of file From 17c38d3794e5121ff541e5d0c790cf1859bd2926 Mon Sep 17 00:00:00 2001 From: leonhartX Date: Sat, 8 Feb 2020 17:47:11 +0900 Subject: [PATCH 2/4] add more erro detail --- src/scm/github.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scm/github.js b/src/scm/github.js index 7142a4f..9288e93 100644 --- a/src/scm/github.js +++ b/src/scm/github.js @@ -144,7 +144,7 @@ class Github { showAlert(`Successfully push to ${context.branch} of ${context.repo.fullName}`); }) .catch(err => { - showAlert('Failed to push', LEVEL_ERROR); + showAlert(`Failed to push: ${err}`, LEVEL_ERROR); }); } @@ -325,7 +325,7 @@ class Github { return this.namespaces; }) .catch((err) => { - showAlert('Failed to get user info.', LEVEL_ERROR); + showAlert(`Failed to get user info: ${err}`, LEVEL_ERROR); }); } From 21faad658b23e73889e3cdcd2f59650a0509d40c Mon Sep 17 00:00:00 2001 From: leonhartX Date: Sat, 8 Feb 2020 17:22:12 +0900 Subject: [PATCH 3/4] fix access token in query parameter --- manifest.json | 2 +- src/gas-api.js | 336 +++++++++++----------- src/gas-hub.js | 523 ++++++++++++++++++---------------- src/scm/bitbucket.js | 426 +++++++++++++++------------- src/scm/github.js | 656 +++++++++++++++++++++++-------------------- src/scm/gitlab.js | 267 ++++++++++-------- src/util.js | 14 + 7 files changed, 1181 insertions(+), 1043 deletions(-) diff --git a/manifest.json b/manifest.json index 5fcfdfe..beb8a10 100644 --- a/manifest.json +++ b/manifest.json @@ -1,5 +1,5 @@ { - "version": "4.0.6", + "version": "4.0.7", "manifest_version": 2, "default_locale": "en", "name": "__MSG_appName__", diff --git a/src/gas-api.js b/src/gas-api.js index ede3d8b..cbef787 100644 --- a/src/gas-api.js +++ b/src/gas-api.js @@ -4,42 +4,42 @@ class Gas { pull(code) { const changed = $('.diff-file:checked').toArray().map(e => e.value); const updatePromises = changed.filter(f => code.scm[f]) - .map((file) => { - const regex = new RegExp(`(.*?)(${context.config.filetype}|\.json|\.html)$`) - const match = file.match(regex); - if (!match || !match[1] || !match[2]) { - showAlert('Unsupported file type.', LEVEL_ERROR); - return; - } - if (match[2] === 'json' && match[1] !== 'appsscript') { - showAlert('Unsupported file type', LEVEL_ERROR); - return; - } - const name = match[1]; - const type = match[2]; + .map((file) => { + const regex = new RegExp(`(.*?)(${context.config.filetype}|\.json|\.html)$`) + const match = file.match(regex); + if (!match || !match[1] || !match[2]) { + showAlert('Unsupported file type.', LEVEL_ERROR); + return; + } + if (match[2] === 'json' && match[1] !== 'appsscript') { + showAlert('Unsupported file type', LEVEL_ERROR); + return; + } + const name = match[1]; + const type = match[2]; - if (!code.gas[file]) { - return () => this.gasCreateFile(name, type) - .then(() => { - return this.gasUpdateFile(name, code.scm[file]); - }) - } else { - return () => this.gasUpdateFile(name, code.scm[file]); - } - }) - .filter(n => n != undefined); + if (!code.gas[file]) { + return () => this.gasCreateFile(name, type) + .then(() => { + return this.gasUpdateFile(name, code.scm[file]); + }) + } else { + return () => this.gasUpdateFile(name, code.scm[file]); + } + }) + .filter(n => n != undefined); const deletePromises = changed.filter(f => !code.scm[f]) - .map((file) => { - const regex = new RegExp(`(.*?)(${context.config.filetype}|\.html)$`) - const match = file.match(regex); - if (!match || !match[1] || !match[2]) { - showAlert('Unknow Error', LEVEL_ERROR); - return; - } - const name = match[1]; - return () => this.gasDeleteFile(name); - }); + .map((file) => { + const regex = new RegExp(`(.*?)(${context.config.filetype}|\.html)$`) + const match = file.match(regex); + if (!match || !match[1] || !match[2]) { + showAlert('Unknow Error', LEVEL_ERROR); + return; + } + const name = match[1]; + return () => this.gasDeleteFile(name); + }); if (updatePromises.length === 0 && deletePromises.length === 0) { showAlert('Nothing to do', LEVEL_WARN); @@ -47,69 +47,109 @@ class Gas { } this.getGasContext() - .then(() => { - return Promise.all(updatePromises.map(f => f())) .then(() => { - return Promise.all(deletePromises.map(f => f())); + return Promise.all(updatePromises.map(f => f())) + .then(() => { + return Promise.all(deletePromises.map(f => f())); + }) }) - }) - .then(() => { - showAlert('Successfully pulled from scm'); - location.reload(); - }) - .catch((err) => { - showAlert(err.message, LEVEL_ERROR); - }); + .then(() => { + showAlert('Successfully pulled from scm'); + location.reload(); + }) + .catch((err) => { + showAlert(err.message, LEVEL_ERROR); + }); } /* - * get project context with google rpc - * this is very volatile since it is just inferred from code - */ + * get project context with google rpc + * this is very volatile since it is just inferred from code + */ getGasContext() { return new Promise((resolve, reject) => { - chrome.storage.local.get(['requestUrl' ,'requestHeaders', 'requestBody', 'gasToken'], resolve); - }) - .then((param) => { - context.gasUrl = param.requestUrl; - context.gasHeaders = param.requestHeaders; - context.gasToken = param.gasToken; - return param.requestBody; - }); + chrome.storage.local.get(['requestUrl', 'requestHeaders', 'requestBody', 'gasToken'], resolve); + }) + .then((param) => { + context.gasUrl = param.requestUrl; + context.gasHeaders = param.requestHeaders; + context.gasToken = param.gasToken; + return param.requestBody; + }); } getGasCode() { return this.getGasContext() - .then((requestBody) => { - return $.ajax({ - url: context.gasUrl, - headers: context.gasHeaders, - method: 'POST', - crossDomain: true, - data: requestBody, - dataType: 'text' + .then((requestBody) => { + return $.ajax({ + url: context.gasUrl, + headers: context.gasHeaders, + method: 'POST', + crossDomain: true, + data: requestBody, + dataType: 'text' + }) }) - }) - .then((response) => { - if (!response.startsWith('//OK')) throw new Error('Init failed'); - //evil eval, but it's simple to get the object since it's not valid json object - const initData = eval(response.slice(4)).filter((e) => { - return typeof(e) === 'object'; - })[0]; - const ids = initData.filter((data) => { return /^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/.test(data) }); - context.projectId = initData[initData.indexOf(ids[0]) + 2]; - if (context.projectId === context.id) { - // for bounded script - context.id = initData[initData.indexOf(ids[0]) + 1]; - } + .then((response) => { + if (!response.startsWith('//OK')) throw new Error('Init failed'); + //evil eval, but it's simple to get the object since it's not valid json object + const initData = eval(response.slice(4)).filter((e) => { + return typeof (e) === 'object'; + })[0]; + const ids = initData.filter((data) => { + return /^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/.test(data) + }); + context.projectId = initData[initData.indexOf(ids[0]) + 2]; + if (context.projectId === context.id) { + // for bounded script + context.id = initData[initData.indexOf(ids[0]) + 1]; + } + + if (context.projectId.length != 33) { + reject(new Error('cant not get project ID')); + } + const promises = ids.map((id) => { + return new Promise((resolve, reject) => { + const payload = `7|1|9|${getBaseUrl()}\|${context.gasToken}|_|getFileContent|l|${id}|${context.id}|${context.projectId}|m|1|2|3|4|1|5|5|6|7|8|9|0|0|`; + $.ajax({ + url: context.gasUrl, + headers: context.gasHeaders, + method: 'POST', + crossDomain: true, + data: payload, + dataType: 'text' + }) + .then((response) => { + if (!response.startsWith('//OK')) reject(new Error('get apps script code failed')); + //evil eval, but it's simple to get the object since it's not valid json object + const codeContent = eval(response.slice(4)).filter((e) => { + return typeof (e) === 'object'; + })[0]; + resolve({ + file: codeContent[codeContent.length - 7], + content: codeContent[codeContent.length - 10], + id: id + }); + }) + .fail(reject); + }) + }); + return Promise.all(promises); + }) + .then((responses) => { + context.fileIds = responses.reduce((hash, elem) => { + if (elem) hash[elem.file] = elem.id; + return hash; + }, {}); + return responses; + }) + } - if (context.projectId.length != 33) { - reject(new Error('cant not get project ID')); - } - const promises = ids.map((id) => { - return new Promise((resolve, reject) => { - const payload = `7|1|9|${getBaseUrl()}\|${context.gasToken}|_|getFileContent|l|${id}|${context.id}|${context.projectId}|m|1|2|3|4|1|5|5|6|7|8|9|0|0|`; - $.ajax({ + gasCreateFile(file, type) { + const typeId = type === context.config.filetype ? 0 : 2; + const payload = `7|1|7|${getBaseUrl()}\|${context.gasToken}|_|makeNewFile|1a|i|${file}|1|2|3|4|2|5|6|7|6|${typeId}|`; + return new Promise((resolve, reject) => { + $.ajax({ url: context.gasUrl, headers: context.gasHeaders, method: 'POST', @@ -117,102 +157,76 @@ class Gas { data: payload, dataType: 'text' }) - .then((response) => { - if (!response.startsWith('//OK')) reject(new Error('get apps script code failed')); - //evil eval, but it's simple to get the object since it's not valid json object - const codeContent = eval(response.slice(4)).filter((e) => { - return typeof(e) === 'object'; - })[0]; - resolve({file : codeContent[codeContent.length - 7], content: codeContent[codeContent.length - 10], id : id }); + .then(resolve) + .fail((err) => { + reject(new Error('Create file failed')) }) - .fail(reject); - }) - }); - return Promise.all(promises); - }) - .then((responses) => { - context.fileIds = responses.reduce((hash, elem) => { - if (elem) hash[elem.file] = elem.id; - return hash; - }, {}); - return responses; - }) - } - - gasCreateFile(file, type) { - const typeId = type === context.config.filetype ? 0 : 2; - const payload = `7|1|7|${getBaseUrl()}\|${context.gasToken}|_|makeNewFile|1a|i|${file}|1|2|3|4|2|5|6|7|6|${typeId}|`; - return new Promise((resolve, reject) => { - $.ajax({ - url: context.gasUrl, - headers: context.gasHeaders, - method: 'POST', - crossDomain: true, - data: payload, - dataType: 'text' }) - .then(resolve) - .fail((err) => {reject(new Error('Create file failed'))}) - }) - .then((response) => { - if (!response.startsWith('//OK')) throw(new Error(`Create file '${file}.${type}' failed`)); - const responseData = eval(response.slice(4)).filter((e) => { - return typeof(e) === 'object'; - })[0]; - const id = responseData.filter((data) => { return /^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/.test(data) }); - if (id.length > 0) { - context.fileIds[file] = id[0]; - return id[0]; - } else { - throw new Error('can not parse response'); - } - }); + .then((response) => { + if (!response.startsWith('//OK')) throw (new Error(`Create file '${file}.${type}' failed`)); + const responseData = eval(response.slice(4)).filter((e) => { + return typeof (e) === 'object'; + })[0]; + const id = responseData.filter((data) => { + return /^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/.test(data) + }); + if (id.length > 0) { + context.fileIds[file] = id[0]; + return id[0]; + } else { + throw new Error('can not parse response'); + } + }); } gasUpdateFile(file, code) { const escapedCode = code.replace(/\\/g, '\\\\').replace(/\|/g, '\\!'); const payload = `7|1|7|${getBaseUrl()}\|${context.gasToken}|_|updateFile|1a|${file}|${escapedCode}|1|2|3|4|2|5|5|6|7|`; let headers = context.gasHeaders; - Object.assign(headers, { 'file-id': context.fileIds[file]}); + Object.assign(headers, { + 'file-id': context.fileIds[file] + }); return new Promise((resolve, reject) => { $.ajax({ - url: context.gasUrl, - headers: headers, - method: 'POST', - crossDomain: true, - data: payload, - dataType: 'text' - }) - .then((response) => { - if (!response.startsWith('//OK')) reject(new Error('Update file failed')); - resolve(); - }) - .fail((err) => { - reject(new Error('Update file failed')); - }); + url: context.gasUrl, + headers: headers, + method: 'POST', + crossDomain: true, + data: payload, + dataType: 'text' + }) + .then((response) => { + if (!response.startsWith('//OK')) reject(new Error('Update file failed')); + resolve(); + }) + .fail((err) => { + reject(new Error('Update file failed')); + }); }); } gasDeleteFile(file) { const payload = `7|1|4|${getBaseUrl()}\|${context.gasToken}|_|deleteFile|1|2|3|4|0|`; let headers = context.gasHeaders; - Object.assign(headers, { 'file-id': context.fileIds[file]}); + Object.assign(headers, { + 'file-id': context.fileIds[file] + }); return new Promise((resolve, reject) => { $.ajax({ - url: context.gasUrl, - headers: headers, - method: 'POST', - crossDomain: true, - data: payload, - dataType: 'text' - }) - .then((response) => { - if (!response.startsWith('//OK')) reject(new Error('Delete file failed')); - resolve(); - }) - .fail((err) => { - reject(new Error('Update file failed')); - }); + url: context.gasUrl, + headers: headers, + method: 'POST', + crossDomain: true, + data: payload, + dataType: 'text' + }) + .then((response) => { + if (!response.startsWith('//OK')) reject(new Error('Delete file failed')); + resolve(); + }) + .fail((err) => { + reject(new Error('Update file failed')); + }); }); } } \ No newline at end of file diff --git a/src/gas-hub.js b/src/gas-hub.js index 41792ba..8c47736 100644 --- a/src/gas-hub.js +++ b/src/gas-hub.js @@ -13,29 +13,29 @@ const observer = new MutationObserver((e) => { $(() => { initPageContent() - .then(initContext) - .then(updateRepo) - .then(updateBranch) - .then(updateGist) - .then(initPageEvent) - .catch((err) => { - switch (err.message) { - case 'need login' : - initLoginContent(); - break; - case 'not match' : - break; - case 'nothing' : - break; - case 'need relogin': - initLoginContent(); - showAlert('Extension has been updated, please relogin', LEVEL_WARN); - break; - default: - showAlert('Unknow Error', LEVEL_ERROR); - break; - } - }); + .then(initContext) + .then(updateRepo) + .then(updateBranch) + .then(updateGist) + .then(initPageEvent) + .catch((err) => { + switch (err.message) { + case 'need login': + initLoginContent(); + break; + case 'not match': + break; + case 'nothing': + break; + case 'need relogin': + initLoginContent(); + showAlert('Extension has been updated, please relogin', LEVEL_WARN); + break; + default: + showAlert(err, LEVEL_ERROR); + break; + } + }); }); function initContext() { @@ -46,86 +46,90 @@ function initContext() { context.id = match[2]; return new Promise((resolve, reject) => { - chrome.storage.sync.get([ - 'scm', - 'token', - 'user', - 'baseUrl', - 'bindRepo', - 'bindBranch', - 'bindType', - 'bindPattern', - 'bindConfig' - ], (item) => { - if (!item.token) { - reject(new Error('need login')); - } - scm = createSCM(item); - context.bindRepo = item.bindRepo || {}; - context.bindBranch = item.bindBranch || {}; - context.bindType = item.bindType || {}; - context.bindPattern = item.bindPattern || {}; - context.bindConfig = item.bindConfig || {}; - context.config = context.bindConfig[context.id] || {}; - context.config.filetype = context.config.filetype || context.bindType[context.id] || '.gs'; - context.config.ignorePattern = context.config.ignorePattern || context.bindPattern[context.id] || []; - context.config.manifestEnabled = context.config.manifestEnabled || false; - context.gist = context.bindRepo[context.id] && context.bindRepo[context.id].gist; - resolve(scm); - }); - }) - .then(scm => { - return scm.getNamespaces() - .then((owners) => { - owners.forEach((owner) => { - let content = `` - $('#new-repo-owner').append(content); + chrome.storage.sync.get([ + 'scm', + 'token', + 'user', + 'baseUrl', + 'bindRepo', + 'bindBranch', + 'bindType', + 'bindPattern', + 'bindConfig' + ], (item) => { + if (!item.token) { + reject(new Error('need login')); + } + scm = createSCM(item); + context.bindRepo = item.bindRepo || {}; + context.bindBranch = item.bindBranch || {}; + context.bindType = item.bindType || {}; + context.bindPattern = item.bindPattern || {}; + context.bindConfig = item.bindConfig || {}; + context.config = context.bindConfig[context.id] || {}; + context.config.filetype = context.config.filetype || context.bindType[context.id] || '.gs'; + context.config.ignorePattern = context.config.ignorePattern || context.bindPattern[context.id] || []; + context.config.manifestEnabled = context.config.manifestEnabled || false; + context.gist = context.bindRepo[context.id] && context.bindRepo[context.id].gist; + resolve(scm); }); - return scm; }) - }) - .then(scm => { - return scm.getRepos(); - }) + .then(scm => { + return scm.getNamespaces() + .then((owners) => { + owners.forEach((owner) => { + let content = `` + $('#new-repo-owner').append(content); + }); + return scm; + }) + }) + .then(scm => { + return scm.getRepos(); + }) } function initPageContent() { return Promise.all([ - $.get(chrome.runtime.getURL('content/button.html')), - $.get(chrome.runtime.getURL('content/menu.html')), - $.get(chrome.runtime.getURL('content/modal.html')) - ]) - .then((content) => { - $('#functionSelect').after(content[0]); - $('body').children().last().after(content[1]); - $('body').children().last().after(content[2]); - }) - .then(() => { - $(document).on('click', '.scm-alert-dismiss', () => { - $('.scm-alert').remove(); + $.get(chrome.runtime.getURL('content/button.html')), + $.get(chrome.runtime.getURL('content/menu.html')), + $.get(chrome.runtime.getURL('content/modal.html')) + ]) + .then((content) => { + $('#functionSelect').after(content[0]); + $('body').children().last().after(content[1]); + $('body').children().last().after(content[2]); + }) + .then(() => { + $(document).on('click', '.scm-alert-dismiss', () => { + $('.scm-alert').remove(); + }); + chrome.runtime.sendMessage({ + cmd: 'tab' + }); }); - chrome.runtime.sendMessage({ cmd: 'tab' }); - }); } function initLoginContent() { $.get(chrome.runtime.getURL('content/login.html')) - .then((content) => { - $('#functionSelect').after(content); - $('#login').hover(() => { - $('#login').addClass('goog-toolbar-menu-button-hover'); - }, () => { - $('#login').removeClass('goog-toolbar-menu-button-hover'); - }); - $('#login').click(() => { - if (chrome.runtime.openOptionsPage) { - chrome.runtime.openOptionsPage(); - } else { - window.open(chrome.runtime.getURL('options/options.html')); - } + .then((content) => { + $('#functionSelect').after(content); + $('#login').hover(() => { + $('#login').addClass('goog-toolbar-menu-button-hover'); + }, () => { + $('#login').removeClass('goog-toolbar-menu-button-hover'); + }); + $('#login').click(() => { + if (chrome.runtime.openOptionsPage) { + chrome.runtime.openOptionsPage(); + } else { + window.open(chrome.runtime.getURL('options/options.html')); + } + }); + chrome.runtime.sendMessage({ + cmd: 'tab' + }); }); - chrome.runtime.sendMessage({ cmd: 'tab' }); - }); } function initPageEvent() { @@ -134,10 +138,10 @@ function initPageEvent() { ['repo', 'branch'].forEach((type) => { const container = $(`.${type}-menu`); const button = $(`#${type}Select`); - if (!container.is(event.target) - && !button.is(event.target) - && container.has(event.target).length === 0 - && button.has(event.target).length == 0) { + if (!container.is(event.target) && + !button.is(event.target) && + container.has(event.target).length === 0 && + button.has(event.target).length == 0) { container.hide(); $(`#${type}Select`).removeClass('goog-toolbar-menu-button-open'); } @@ -182,10 +186,10 @@ function initPageEvent() { $(document).on('click', `#scm-create-${type}`, () => { changeModalState(type, false); scm[`create${type.capitalize()}`]() - .then(window[`handle${type.capitalize()}Created`]) - .catch(err => { - showAlert(err.message, LEVEL_ERROR); - }) + .then(window[`handle${type.capitalize()}Created`]) + .catch(err => { + showAlert(err.message, LEVEL_ERROR); + }) }); $(document).on('input propertychange', `#new-${type}-name`, (event) => { @@ -211,8 +215,12 @@ function initPageEvent() { ['pull', 'push'].forEach(type => { $(document).on('click', `#${type}-button`, () => { prepareCode() - .then((data) => { showDiff(data, type); }) //get more performance over callback - .catch((err) => { showAlert(err.message, LEVEL_ERROR); }); + .then((data) => { + showDiff(data, type); + }) //get more performance over callback + .catch((err) => { + showAlert(err.message, LEVEL_ERROR); + }); }); }) @@ -225,11 +233,13 @@ function initPageEvent() { $(document).on('click', '#save-config', () => { context.config.filetype = $('#filetype').val(); - context.config.manifestEnabled = $('#manage-manifest').prop( "checked" ); + context.config.manifestEnabled = $('#manage-manifest').prop("checked"); context.config.ignorePattern = $('#ignore-pattern').val().split(';').filter(p => p !== ''); context.bindConfig[context.id] = context.config; try { - chrome.storage.sync.set({ bindConfig: context.bindConfig }); + chrome.storage.sync.set({ + bindConfig: context.bindConfig + }); changeModalState('config', false); } catch (err) { showAlert(err.message, LEVEL_ERROR); @@ -245,18 +255,18 @@ function initPageEvent() { let content; let label; switch (type) { - case 'repo' : + case 'repo': if (context.repo && target.text() === context.repo.fullName) return; //update context.repo with name and fullName const fullName = target.attr('data'); content = { - fullName : fullName, + fullName: fullName, gist: fullName === 'gist' } label = fullName; context.gist = content.gist; break; - case 'branch' : + case 'branch': if (context[type] && target.text() === context[type]) return; content = target.attr('data'); label = target.attr('data'); @@ -266,8 +276,12 @@ function initPageEvent() { } context[type] = content; const bindName = `bind${type.capitalize()}`; - Object.assign(context[bindName], { [context.id] : content }); - chrome.storage.sync.set({ [bindName]: context[bindName] }, () => { + Object.assign(context[bindName], { + [context.id]: content + }); + chrome.storage.sync.set({ + [bindName]: context[bindName] + }, () => { $(`#${type}Select`).removeClass('goog-toolbar-menu-button-open'); $(`.${type}-menu`).hide(); if (type === 'repo') { @@ -286,31 +300,31 @@ function initPageEvent() { function prepareCode() { return Promise.all([gas.getGasCode(), scm.getCode()]) - .then((data) => { - const re = new RegExp(`\\${context.config.filetype}$`); - const files = $('.item > .gwt-Label').toArray().reduce((hash, e) => { - if (context.config.manifestEnabled && e.title === 'appsscript.json') { - hash['appsscript'] = 'appsscript.json'; - } - const match = e.title.match(/(.*?)\.(gs|html)$/); - if (!match || !match[1] || !match[2]) return hash; - hash[match[1]] = match[0]; - return hash; - }, {}); - const code = { - gas: data[0].reduce((hash, elem) => { - if (elem && files[elem.file]) hash[files[elem.file].replace(/\.gs$/, context.config.filetype)] = elem.content; - return hash; - }, {}), - scm: data[1].reduce((hash, elem) => { - if (elem) { - hash[elem.file] = elem.content; + .then((data) => { + const re = new RegExp(`\\${context.config.filetype}$`); + const files = $('.item > .gwt-Label').toArray().reduce((hash, e) => { + if (context.config.manifestEnabled && e.title === 'appsscript.json') { + hash['appsscript'] = 'appsscript.json'; } + const match = e.title.match(/(.*?)\.(gs|html)$/); + if (!match || !match[1] || !match[2]) return hash; + hash[match[1]] = match[0]; return hash; - }, {}) - } - return code; - }) + }, {}); + const code = { + gas: data[0].reduce((hash, elem) => { + if (elem && files[elem.file]) hash[files[elem.file].replace(/\.gs$/, context.config.filetype)] = elem.content; + return hash; + }, {}), + scm: data[1].reduce((hash, elem) => { + if (elem) { + hash[elem.file] = elem.content; + } + return hash; + }, {}) + } + return code; + }) } function showDiff(code, type) { @@ -324,49 +338,54 @@ function showDiff(code, type) { const gasFiles = Object.keys(code.gas); const scmFiles = Object.keys(code.scm); let diff = scmFiles.filter((e) => { - return gasFiles.indexOf(e) < 0; - }) - .concat(gasFiles) - .filter(file => { - if (context.config.manifestEnabled && file === 'appsscript.json') { - return true; - } - for (let i = 0; i < context.config.ignorePattern.length; i ++) { - let p = new RegExp(context.config.ignorePattern[i]); - if (p.test(file)) return false; - } - const regex = new RegExp(`(.*?)(${context.config.filetype}|\.html)$`) - const match = file.match(regex); - return match && match[1] && match[2]; - }) - .reduce((diff, file) => { - let mode = null; - if (!oldCode[file]) { - mode = 'new file mode 100644'; - } else if (!newCode[file]) { - if (file === 'appsscript.json') { - return diff; //can not delete manifest file + return gasFiles.indexOf(e) < 0; + }) + .concat(gasFiles) + .filter(file => { + if (context.config.manifestEnabled && file === 'appsscript.json') { + return true; } - mode = 'deleted file mode 100644'; - } - let fileDiff = JsDiff.createPatch(file, oldCode[file] || '', newCode[file] || ''); - if (fileDiff.indexOf('@@') < 0) return diff; //no diff - let diffArr = fileDiff.split('\n'); - diffArr.splice(0, 2, `diff --git a/${file} b/${file}`); - if (mode) { - diffArr.splice(1, 0, mode); - } - fileDiff = diffArr.join('\n'); - return diff + fileDiff; - }, ''); + for (let i = 0; i < context.config.ignorePattern.length; i++) { + let p = new RegExp(context.config.ignorePattern[i]); + if (p.test(file)) return false; + } + const regex = new RegExp(`(.*?)(${context.config.filetype}|\.html)$`) + const match = file.match(regex); + return match && match[1] && match[2]; + }) + .reduce((diff, file) => { + let mode = null; + if (!oldCode[file]) { + mode = 'new file mode 100644'; + } else if (!newCode[file]) { + if (file === 'appsscript.json') { + return diff; //can not delete manifest file + } + mode = 'deleted file mode 100644'; + } + let fileDiff = JsDiff.createPatch(file, oldCode[file] || '', newCode[file] || ''); + if (fileDiff.indexOf('@@') < 0) return diff; //no diff + let diffArr = fileDiff.split('\n'); + diffArr.splice(0, 2, `diff --git a/${file} b/${file}`); + if (mode) { + diffArr.splice(1, 0, mode); + } + fileDiff = diffArr.join('\n'); + return diff + fileDiff; + }, ''); if (diff === '') { showAlert('Everything already up-to-date', LEVEL_WARN); return; } - const diffHtml = new Diff2HtmlUI({diff : diff}); - diffHtml.draw('.scm-diff', {inputFormat: 'json', showFiles: false}); + const diffHtml = new Diff2HtmlUI({ + diff: diff + }); + diffHtml.draw('.scm-diff', { + inputFormat: 'json', + showFiles: false + }); diffHtml.highlightCode('.scm-diff'); $('.d2h-file-name-wrapper').each((i, e) => { const filename = $(e).children('.d2h-file-name').text(); @@ -410,24 +429,24 @@ function updateRepo(repos) { if (scm.canUseGist) { $('.repo-menu').append('
gist
'); } - + repos.forEach((repo) => { let content = `
${repo}
` $('.repo-menu').append(content); }); if (context.repo) { $('#scm-bind-repo').text(`Repo: ${context.repo.fullName}`); - + //highlight current repo in repos list let repoItems = document.getElementsByClassName('scm-item goog-menuitem'); for (let i = 0; i < repoItems.length; i++) { let currentItem = repoItems[i]; if (context.repo.fullName === currentItem.innerText) { - currentItem.style.background = "lightgrey"; - break; + currentItem.style.background = "lightgrey"; + break; } } - + return context.repo.fullName; } return null; @@ -438,24 +457,28 @@ function updateGist() { return null; } return scm.getAllGists() - .then((gists) => { - $('.branch-menu').empty().append('
Create new gist
'); - gists.forEach((gist) => { - let tooltip = gist.description === '' ? 'no description' : gist.description; - let content = `
${gist.id}
` - $('.branch-menu').append(content); + .then((gists) => { + $('.branch-menu').empty().append('
Create new gist
'); + gists.forEach((gist) => { + let tooltip = gist.description === '' ? 'no description' : gist.description; + let content = `
${gist.id}
` + $('.branch-menu').append(content); + }); + let gist = context.bindBranch[context.id]; + if ($.inArray(gist, gists.map(gist => gist.id)) < 0) { + gist = ''; + } + $('#scm-bind-branch').text(`Gist: ${gist}`); + //update context and storage + context.branch = gist; + Object.assign(context.bindBranch, { + [context.id]: gist + }); + chrome.storage.sync.set({ + bindBranch: context.bindBranch + }); + return gist; }); - let gist = context.bindBranch[context.id]; - if ($.inArray(gist, gists.map(gist => gist.id)) < 0) { - gist = ''; - } - $('#scm-bind-branch').text(`Gist: ${gist}`); - //update context and storage - context.branch = gist; - Object.assign(context.bindBranch, { [context.id] : gist }); - chrome.storage.sync.set({ bindBranch: context.bindBranch }); - return gist; - }); } function updateBranch() { @@ -463,68 +486,72 @@ function updateBranch() { return null; } return scm.getAllBranches() - .then((branches) => { - $('.branch-menu').empty().append('
Create new branch
'); - branches.forEach((branch) => { - let content = `
${branch.name}
` - $('.branch-menu').append(content); - }); - let branch = context.bindBranch[context.id]; - if (branches.length === 0) { - branch = ''; - if (scm.name === 'github') { - showAlert('This repository is empty, try to create a new branch such as [master] in Github', LEVEL_WARN); - } else { - showAlert('This repository is empty, first create a new branch', LEVEL_WARN); + .then((branches) => { + $('.branch-menu').empty().append('
Create new branch
'); + branches.forEach((branch) => { + let content = `
${branch.name}
` + $('.branch-menu').append(content); + }); + let branch = context.bindBranch[context.id]; + if (branches.length === 0) { + branch = ''; + if (scm.name === 'github') { + showAlert('This repository is empty, try to create a new branch such as [master] in Github', LEVEL_WARN); + } else { + showAlert('This repository is empty, first create a new branch', LEVEL_WARN); + } + } else if ($.inArray(branch, branches.map(branch => branch.name)) < 0) { + branch = ($.inArray("master", branches.map(branch => branch.name)) >= 0) ? 'master' : branches[0].name; } - } else if ($.inArray(branch, branches.map(branch => branch.name)) < 0) { - branch = ($.inArray("master", branches.map(branch => branch.name)) >= 0) ? 'master' : branches[0].name; - } - $('#scm-bind-branch').text(`Branch: ${branch}`); - //update context and storage - context.branch = branch; - Object.assign(context.bindBranch, { [context.id] : branch }); - chrome.storage.sync.set({ bindBranch: context.bindBranch }); - return branch; - }); + $('#scm-bind-branch').text(`Branch: ${branch}`); + //update context and storage + context.branch = branch; + Object.assign(context.bindBranch, { + [context.id]: branch + }); + chrome.storage.sync.set({ + bindBranch: context.bindBranch + }); + return branch; + }); } function handleRepoCreated(repo) { return scm.getRepos() - .then(updateRepo) - .then(updateBranch) - .then(() => { - $('#new-repo-name').val(''); - $('#new-repo-desc').val(''); - $('#new-repo-type').val('public'); - showAlert(`Successfully create new repository ${repo}`); - }) - .catch(() => { - throw new Error('Repository created, but failed to show the new repository.'); - }); + .then(updateRepo) + .then(updateBranch) + .then(() => { + $('#new-repo-name').val(''); + $('#new-repo-desc').val(''); + $('#new-repo-type').val('public'); + showAlert(`Successfully create new repository ${repo}`); + }) + .catch(() => { + throw new Error('Repository created, but failed to show the new repository.'); + }); } function handleBranchCreated(branch) { return updateBranch() - .then(() => { - $('#new-branch-name').val(''); - showAlert(`Successfully create new branch: ${branch}`); - }) - .catch(() => { - throw new Error('Branch created, but failed to show the new branch.'); - }); + .then(() => { + $('#new-branch-name').val(''); + showAlert(`Successfully create new branch: ${branch}`); + }) + .catch(() => { + throw new Error('Branch created, but failed to show the new branch.'); + }); } function handleGistCreated() { return updateGist() - .then(() => { - $('#new-gist-name').val(''); - $('#new-gist-public').val('public'); - showAlert(`Successfully create new gist.`); - }) - .catch(err => { - throw new Error('Gist created, but failed to show the new gist.'); - }); + .then(() => { + $('#new-gist-name').val(''); + $('#new-gist-public').val('public'); + showAlert(`Successfully create new gist.`); + }) + .catch(err => { + throw new Error('Gist created, but failed to show the new gist.'); + }); } function getBaseUrl() { @@ -560,15 +587,17 @@ function changeButtonState(type, value) { * level: info, warning, error * but the class is promo. info, warning */ -function showAlert(message, level=LEVEL_INFO) { +function showAlert(message, level = LEVEL_INFO) { $.get(chrome.runtime.getURL('content/alert.html')) - .then((content) => { - observer.disconnect(); - $('#docs-butterbar-container').empty().append(content.replace(/_LEVEL_/g, level).replace(/_MESSAGE_/, message)); - observer.observe(document.getElementById('docs-butterbar-container'), { childList: true }); - }) + .then((content) => { + observer.disconnect(); + $('#docs-butterbar-container').empty().append(content.replace(/_LEVEL_/g, level).replace(/_MESSAGE_/, message)); + observer.observe(document.getElementById('docs-butterbar-container'), { + childList: true + }); + }) } -String.prototype.capitalize = function() { - return this.charAt(0).toUpperCase() + this.slice(1); -} +String.prototype.capitalize = function () { + return this.charAt(0).toUpperCase() + this.slice(1); +} \ No newline at end of file diff --git a/src/scm/bitbucket.js b/src/scm/bitbucket.js index 9e2d643..695218f 100644 --- a/src/scm/bitbucket.js +++ b/src/scm/bitbucket.js @@ -15,34 +15,36 @@ class Bitbucket { get canUseGist() { return false; - } + } getAccessToken() { return new Promise((resolve, reject) => { - $.ajax({ - url: 'https://bitbucket.org/site/oauth2/access_token', - headers: { - Authorization: `Basic RmZIVE02ZnN5NDJQQlJDRjRQOmVDZDN0TTh5TUpUeTJSMld4bTJWUzZoYWVKdnpuNzdw` - }, - method: 'POST', - dataType: 'json', - contentType: 'application/x-www-form-urlencoded', - data: { - grant_type: 'refresh_token', - refresh_token: this.token - } + $.ajax({ + url: 'https://bitbucket.org/site/oauth2/access_token', + headers: { + Authorization: `Basic RmZIVE02ZnN5NDJQQlJDRjRQOmVDZDN0TTh5TUpUeTJSMld4bTJWUzZoYWVKdnpuNzdw` + }, + method: 'POST', + dataType: 'json', + contentType: 'application/x-www-form-urlencoded', + data: { + grant_type: 'refresh_token', + refresh_token: this.token + } + }) + .then(resolve) + .fail(reject) + }) + .then(response => { + chrome.storage.sync.set({ + token: response.refresh_token + }); + this.accessToken = response.access_token; + return response.access_token; + }) + .catch(err => { + showAlert(`Failed to refresh access token: ${err}`, LEVEL_ERROR); }) - .then(resolve) - .fail(reject) - }) - .then(response => { - chrome.storage.sync.set({ token: response.refresh_token }); - this.accessToken = response.access_token; - return response.access_token; - }) - .catch(err => { - showAlert('Failed to refresh access token.', LEVEL_ERROR); - }) } commitFiles(repo, branch, parent, files, deleteFiles, comment) { @@ -62,131 +64,136 @@ class Bitbucket { data.parents = parent; } $.ajax({ - url: `${this.baseUrl}/repositories/${repo}/src`, - headers: { - 'Authorization': `Bearer ${this.accessToken}` - }, - contentType: 'application/x-www-form-urlencoded', - method: 'POST', - crossDomain: true, - traditional: true, - data: data, - }) - .then(resolve) - .fail(reject); + url: `${this.baseUrl}/repositories/${repo}/src`, + headers: { + 'Authorization': `Bearer ${this.accessToken}` + }, + contentType: 'application/x-www-form-urlencoded', + method: 'POST', + crossDomain: true, + traditional: true, + data: data, + }) + .then(resolve) + .fail(reject); }); } - push(code){ + push(code) { const changed = $('.diff-file:checked').toArray().map(elem => elem.value); const files = changed.filter(f => code.gas[f]).map(f => { - return { name: f, content: code.gas[f] } + return { + name: f, + content: code.gas[f] + } }); const deleteFiles = changed.filter(f => !code.gas[f]); const comment = $('#commit-comment').val(); this.commitFiles(context.repo.fullName, context.branch, null, files, deleteFiles, comment) - .then(() => { - showAlert(`Successfully push to ${context.branch} of ${context.repo.fullName}`); - }) - .catch((err) => { - showAlert('Failed to push', LEVEL_ERROR); - }); + .then(() => { + showAlert(`Successfully push to ${context.branch} of ${context.repo.fullName}`); + }) + .catch((err) => { + showAlert('Failed to push', LEVEL_ERROR); + }); } getAllBranches() { return this.getAccessToken() - .then(accessToken => { - return getAllItems(Promise.resolve( - { - token: accessToken, - items: [], - url: `${this.baseUrl}/repositories/${context.repo.fullName}/refs/branches?access_token=${accessToken}` - }), - this.followPaginate, - 'bitbucket' - ); - }); + .then(accessToken => { + return getAllItems(Promise.resolve({ + token: accessToken, + items: [], + url: `${this.baseUrl}/repositories/${context.repo.fullName}/refs/branches?access_token=${accessToken}` + }), + this.followPaginate, + 'bitbucket' + ); + }); } getCode() { return this.getAccessToken() - .then(accessToken => { - return $.getJSON( - `${this.baseUrl}/repositories/${context.repo.fullName}/refs/branches/${context.branch}`, - { access_token: accessToken } - ) - }) - .then(response => { - return getAllItems(Promise.resolve( - { - token: this.accessToken, - items: [], - urls: [], - url: `${this.baseUrl}/repositories/${context.repo.fullName}/src/${response.target.hash}/?access_token=${this.accessToken}` - }), - this.followDirectory, - 'bitbucket' - ) + .then(accessToken => { + return $.getJSON( + `${this.baseUrl}/repositories/${context.repo.fullName}/refs/branches/${context.branch}`, { + access_token: accessToken + } + ) + }) .then(response => { - const promises = response.map(src => { - return new Promise((resolve, reject) => { - $.get(src.links.self.href, { access_token: this.accessToken }) - .then(content => { - resolve({ file: src.path, content: content}); - }) - .fail(reject) + return getAllItems(Promise.resolve({ + token: this.accessToken, + items: [], + urls: [], + url: `${this.baseUrl}/repositories/${context.repo.fullName}/src/${response.target.hash}/?access_token=${this.accessToken}` + }), + this.followDirectory, + 'bitbucket' + ) + .then(response => { + const promises = response.map(src => { + return new Promise((resolve, reject) => { + $.get(src.links.self.href, { + access_token: this.accessToken + }) + .then(content => { + resolve({ + file: src.path, + content: content + }); + }) + .fail(reject) + }); + }); + return Promise.all(promises); }); - }); - return Promise.all(promises); }); - }); } getNamespaces() { return this.getAccessToken() - .then(accessToken => { - return getAllItems(Promise.resolve( - { - token: accessToken, - items: [], - url: `${this.baseUrl}/teams?access_token=${accessToken}&role=contributor` - }), - this.followPaginate, - 'bitbucket' - ); - }) - .then(teams => { - this.namespaces = [this.user].concat(teams.map(team => team.username)); - return this.namespaces; - }) - .catch((err) => { - showAlert('Failed to get user info.', LEVEL_ERROR); - }); + .then(accessToken => { + return getAllItems(Promise.resolve({ + token: accessToken, + items: [], + url: `${this.baseUrl}/teams?access_token=${accessToken}&role=contributor` + }), + this.followPaginate, + 'bitbucket' + ); + }) + .then(teams => { + this.namespaces = [this.user].concat(teams.map(team => team.username)); + return this.namespaces; + }) + .catch((err) => { + showAlert('Failed to get user info.', LEVEL_ERROR); + }); } getRepos() { return this.getAccessToken() - .then(accessToken => { - return getAllItems(Promise.resolve( - { - token: accessToken, - items: [], - url: `${this.baseUrl}/repositories/?access_token=${accessToken}&q=scm="git"&role=contributor` - }), - this.followPaginate, - 'bitbucket' - ); - }) - .then(response => { - const repos = response.map(repo => repo.full_name); - //if current bind still existed, use it - const repo = context.bindRepo[context.id]; - if (repo && $.inArray(repo.fullName, repos) >= 0) { - context.repo = repo; - } - return repos; - }); + .then(accessToken => { + return getAllItems(Promise.resolve({ + token: accessToken, + items: [], + url: `${this.baseUrl}/repositories/?access_token=${accessToken}&q=scm="git"&role=contributor` + }), + this.followPaginate, + 'bitbucket' + ); + }) + .then(response => { + const repos = response.map(repo => repo.full_name); + //if current bind still existed, use it + const repo = context.bindRepo[context.id]; + if (repo && $.inArray(repo.fullName, repos) >= 0) { + context.repo = repo; + } + return repos; + }); } createRepo() { @@ -196,111 +203,126 @@ class Bitbucket { const isPrivate = $('#new-repo-type').val() !== 'public'; const payload = { scm: 'git', - description : desc, + description: desc, is_private: isPrivate } if (!name || name === '') return; return this.getAccessToken() - .then(() => { - return $.ajax({ - url: `${this.baseUrl}/repositories/${owner}/${name}`, - headers: { - 'Authorization': `Bearer ${this.accessToken}` - }, - method: 'POST', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) - }) - }) - .then(response => { - const repo = { - fullName : response.full_name - }; - context.repo = repo; - Object.assign(context.bindRepo, { [context.id] : repo }); - if (context.bindBranch[context.id]) { - delete context.bindBranch[context.id]; - } - chrome.storage.sync.set({ bindRepo: context.bindRepo }); - return response.full_name; - }) - .then(repo => { - return this.commitFiles(repo, 'master', null, [{name: "README.md", content: "initialed by gas-github"}], null, 'initial commit') .then(() => { - return repo; + return $.ajax({ + url: `${this.baseUrl}/repositories/${owner}/${name}`, + headers: { + 'Authorization': `Bearer ${this.accessToken}` + }, + method: 'POST', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }) + }) + .then(response => { + const repo = { + fullName: response.full_name + }; + context.repo = repo; + Object.assign(context.bindRepo, { + [context.id]: repo + }); + if (context.bindBranch[context.id]) { + delete context.bindBranch[context.id]; + } + chrome.storage.sync.set({ + bindRepo: context.bindRepo + }); + return response.full_name; + }) + .then(repo => { + return this.commitFiles(repo, 'master', null, [{ + name: "README.md", + content: "initialed by gas-github" + }], null, 'initial commit') + .then(() => { + return repo; + }); + }) + .catch((err) => { + throw new Error('Failed to create new repository.'); }); - }) - .catch((err) => { - throw new Error('Failed to create new repository.'); - }); } createBranch() { const branch = $('#new-branch-name').val(); if (!branch || branch === '') return; return this.getAccessToken() - .then(() => { - return $.getJSON( - `${this.baseUrl}/repositories/${context.repo.fullName}/refs/branches/${context.branch}`, - { access_token: this.accessToken } - ); - }) - .then(res => { - const parent = res.target? res.target.hash : null; - return this.commitFiles(context.repo.fullName, branch, parent, [], null, `create new branch ${branch}`); - }) - .then(() => { - context.branch = branch; - Object.assign(context.bindBranch, { [context.id] : branch }); - chrome.storage.sync.set({ bindBranch: context.bindBranch }); - return branch; - }) - .catch(err => { - throw new Error('Failed to create new branch.'); - }); + .then(() => { + return $.getJSON( + `${this.baseUrl}/repositories/${context.repo.fullName}/refs/branches/${context.branch}`, { + access_token: this.accessToken + } + ); + }) + .then(res => { + const parent = res.target ? res.target.hash : null; + return this.commitFiles(context.repo.fullName, branch, parent, [], null, `create new branch ${branch}`); + }) + .then(() => { + context.branch = branch; + Object.assign(context.bindBranch, { + [context.id]: branch + }); + chrome.storage.sync.set({ + bindBranch: context.bindBranch + }); + return branch; + }) + .catch(err => { + throw new Error('Failed to create new branch.'); + }); } followPaginate(data) { return new Promise((resolve, reject) => { $.getJSON(data.url) - .then(response => { - data.items = data.items.concat(response.values); - const link = response.next; - let url = null; - if (link) { - url = link; - } - resolve({ items: data.items, url: url }); - }) - .fail(reject); + .then(response => { + data.items = data.items.concat(response.values); + const link = response.next; + let url = null; + if (link) { + url = link; + } + resolve({ + items: data.items, + url: url + }); + }) + .fail(reject); }) } followDirectory(data) { return new Promise((resolve, reject) => { $.getJSON(data.url) - .then(response => { - const dirs = response.values.filter(src => { - return src.type === 'commit_directory'; - }).map(dir => { - return `${dir.links.self.href}?access_token=${data.token}`; + .then(response => { + const dirs = response.values.filter(src => { + return src.type === 'commit_directory'; + }).map(dir => { + return `${dir.links.self.href}?access_token=${data.token}`; + }) + const re = new RegExp(`(\\${context.config.filetype}|\\.html${context.config.manifestEnabled ? '|^appsscript.json' : ''})$`); + const files = response.values.filter(src => { + return src.type === 'commit_file' && re.test(src.path); + }); + data.items = data.items.concat(files); + data.urls = data.urls.concat(dirs); + let link = response.next; + if (link) { + data.urls.push(`${link}&access_token=${data.token}`); + } + data.url = data.urls.shift(); + resolve(data); }) - const re = new RegExp(`(\\${context.config.filetype}|\\.html${context.config.manifestEnabled ? '|^appsscript.json' : ''})$`); - const files = response.values.filter(src => { - return src.type === 'commit_file' && re.test(src.path); - }); - data.items = data.items.concat(files); - data.urls = data.urls.concat(dirs); - let link = response.next; - if (link) { - data.urls.push(`${link}&access_token=${data.token}`); - } - data.url = data.urls.shift(); - resolve(data); - }) - .fail(reject); + .fail(reject); }) } -} +} \ No newline at end of file diff --git a/src/scm/github.js b/src/scm/github.js index 63bacc2..7142a4f 100644 --- a/src/scm/github.js +++ b/src/scm/github.js @@ -16,7 +16,8 @@ class Github { return true; } - push(code){ + + push(code) { if (context.gist) return this.pushToGist(code); return this.pushToRepo(code); } @@ -29,19 +30,22 @@ class Github { encoding: 'utf-8' }; return $.ajax({ - url: `${this.baseUrl}/repos/${context.repo.fullName}/git/blobs`, - headers: { - 'Authorization': `token ${this.accessToken}` - }, - method: 'POST', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) - }) - .then(response => { - return {file: file, blob: response}; - }) + url: `${this.baseUrl}/repos/${context.repo.fullName}/git/blobs`, + headers: { + 'Authorization': `token ${this.accessToken}` + }, + method: 'POST', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }) + .then(response => { + return { + file: file, + blob: response + }; + }) }); if (changed.length === 0) { showAlert('Nothing to do', LEVEL_WARN); @@ -49,39 +53,66 @@ class Github { } Promise.all([ - Promise.all(promises), - $.getJSON( - `${this.baseUrl}/repos/${context.repo.fullName}/branches/${context.branch}`, - { access_token: this.accessToken } - ) - ]) - .then(responses => { - return $.getJSON( - responses[1].commit.commit.tree.url, - { - recursive: 1, - access_token: this.accessToken - } - ) - .then(baseTree => { - const tree = responses[0].map((data) => { - return { - path: data.file, - mode: '100644', - type: 'blob', - sha: data.blob.sha - } - }) - .concat(baseTree.tree.filter((t) => { - return (t.type != 'tree') && (changed.indexOf(t.path) < 0); - })); - return { - tree: tree - }; + Promise.all(promises), + getGitHubJSON( + `${this.baseUrl}/repos/${context.repo.fullName}/branches/${context.branch}`, + this.accessToken) + ]) + .then(responses => { + return getGitHubJSON( + responses[1].commit.commit.tree.url, + this.accessToken, { + recursive: 1 + } + ) + .then(baseTree => { + const tree = responses[0].map((data) => { + return { + path: data.file, + mode: '100644', + type: 'blob', + sha: data.blob.sha + } + }) + .concat(baseTree.tree.filter((t) => { + return (t.type != 'tree') && (changed.indexOf(t.path) < 0); + })); + return { + tree: tree + }; + }) + .then(payload => { + return $.ajax({ + url: `${this.baseUrl}/repos/${context.repo.fullName}/git/trees`, + headers: { + 'Authorization': `token ${this.accessToken}` + }, + method: 'POST', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }); + }) + .then(response => { + return Object.assign(response, { + parent: responses[1].commit.sha + }) + }) + .fail(err => { + throw err; + }); }) - .then(payload => { + .then(response => { + const payload = { + message: $('#commit-comment').val(), + tree: response.sha, + parents: [ + response.parent + ] + }; return $.ajax({ - url: `${this.baseUrl}/repos/${context.repo.fullName}/git/trees`, + url: `${this.baseUrl}/repos/${context.repo.fullName}/git/commits`, headers: { 'Authorization': `token ${this.accessToken}` }, @@ -93,55 +124,28 @@ class Github { }); }) .then(response => { - return Object.assign(response, { parent: responses[1].commit.sha }) + const payload = { + force: true, + sha: response.sha + }; + return $.ajax({ + url: `${this.baseUrl}/repos/${context.repo.fullName}/git/refs/heads/${context.branch}`, + headers: { + 'Authorization': `token ${this.accessToken}` + }, + method: 'PATCH', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }); }) - .fail(err => { - throw err; - }); - }) - .then(response => { - const payload = { - message: $('#commit-comment').val(), - tree: response.sha, - parents: [ - response.parent - ] - }; - return $.ajax({ - url: `${this.baseUrl}/repos/${context.repo.fullName}/git/commits`, - headers: { - 'Authorization': `token ${this.accessToken}` - }, - method: 'POST', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) - }); - }) - .then(response => { - const payload = { - force: true, - sha: response.sha - }; - return $.ajax({ - url: `${this.baseUrl}/repos/${context.repo.fullName}/git/refs/heads/${context.branch}`, - headers: { - 'Authorization': `token ${this.accessToken}` - }, - method: 'PATCH', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) + .then(() => { + showAlert(`Successfully push to ${context.branch} of ${context.repo.fullName}`); + }) + .catch(err => { + showAlert('Failed to push', LEVEL_ERROR); }); - }) - .then(() => { - showAlert(`Successfully push to ${context.branch} of ${context.repo.fullName}`); - }) - .catch(err => { - showAlert('Failed to push', LEVEL_ERROR); - }); } pushToGist(code) { @@ -165,27 +169,31 @@ class Github { payload.description = $('#gist-desc').val(); } return $.ajax({ - url: `${this.baseUrl}/gists/${context.branch}`, - headers: { - 'Authorization': `token ${this.accessToken}` - }, - method: 'PATCH', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) - }) - .then(() => { - showAlert(`Successfully update gist: ${context.branch}`); - }) - .fail(err => { - showAlert('Failed to update', LEVEL_ERROR); - }); + url: `${this.baseUrl}/gists/${context.branch}`, + headers: { + 'Authorization': `token ${this.accessToken}` + }, + method: 'PATCH', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }) + .then(() => { + showAlert(`Successfully update gist: ${context.branch}`); + }) + .fail(err => { + showAlert(`Failed to update: ${err}`, LEVEL_ERROR); + }); } getAllGists() { return getAllItems( - Promise.resolve({ items: [], url: `${this.baseUrl}/users/${this.user}/gists?access_token=${this.accessToken}` }), + Promise.resolve({ + accessToken: this.accessToken, + items: [], + url: `${this.baseUrl}/users/${this.user}/gists` + }), this.followPaginate, 'github' ); @@ -193,7 +201,11 @@ class Github { getAllBranches() { return getAllItems( - Promise.resolve({ items: [], url: `${this.baseUrl}/repos/${context.repo.fullName}/branches?access_token=${this.accessToken}` }), + Promise.resolve({ + accessToken: this.accessToken, + items: [], + url: `${this.baseUrl}/repos/${context.repo.fullName}/branches` + }), this.followPaginate, 'github' ); @@ -206,104 +218,115 @@ class Github { getRepoCode() { return new Promise((resolve, reject) => { - $.getJSON( - `${this.baseUrl}/repos/${context.repo.fullName}/branches/${context.branch}`, - { access_token: this.accessToken } - ) - .then(resolve) - .fail(reject); - }) - .then(response => { - return $.getJSON( - `${this.baseUrl}/repos/${context.repo.fullName}/git/trees/${response.commit.commit.tree.sha}`, - { recursive: 1, access_token: this.accessToken } - ); - }) - .then(response => { - const re = new RegExp(`(\\${context.config.filetype}|\\.html${context.config.manifestEnabled ? '|^appsscript.json' : ''})$`); - const promises = response.tree.filter((tree) => { - return tree.type === 'blob' && re.test(tree.path); + getGitHubJSON( + `${this.baseUrl}/repos/${context.repo.fullName}/branches/${context.branch}`, + this.accessToken) + .then(resolve) + .fail(reject); + }) + .then(response => { + return getGitHubJSON( + `${this.baseUrl}/repos/${context.repo.fullName}/git/trees/${response.commit.commit.tree.sha}`, + this.accessToken, { + recursive: 1 + } + ); }) - .map(tree => { - return new Promise((resolve, reject) => { - $.getJSON(tree.url, { access_token: this.accessToken }) - .then((content) => { - resolve({ file: tree.path, content: decodeURIComponent(escape(atob(content.content))) }); + .then(response => { + const re = new RegExp(`(\\${context.config.filetype}|\\.html${context.config.manifestEnabled ? '|^appsscript.json' : ''})$`); + const promises = response.tree.filter((tree) => { + return tree.type === 'blob' && re.test(tree.path); }) - .fail(reject) - }); + .map(tree => { + return new Promise((resolve, reject) => { + getGitHubJSON(tree.url, this.accessToken) + .then((content) => { + resolve({ + file: tree.path, + content: decodeURIComponent(escape(atob(content.content))) + }); + }) + .fail(reject) + }); + }); + return Promise.all(promises); }); - return Promise.all(promises); - }); } getGistCode() { return new Promise((resolve, reject) => { - $.getJSON( - `${this.baseUrl}/gists/${context.branch}`, - { access_token: this.accessToken } - ) - .then(resolve) - .fail(reject); - }) - .then((response) => { - const promises = Object.keys(response.files).map((filename) => { - let file = response.files[filename]; - return new Promise((resolve, reject) => { - if (file.truncated) { - $.getJSON(file.raw_url, {access_token: this.accessToken }) - .then((content) => { - resolve({ file: filename, content: content}); - }) - .fail(reject) - } else { - resolve({file: filename, content: file.content}); - } + getGitHubJSON(`${this.baseUrl}/gists/${context.branch}`, this.accessToken) + .then(resolve) + .fail(reject); + }) + .then((response) => { + const promises = Object.keys(response.files).map((filename) => { + let file = response.files[filename]; + return new Promise((resolve, reject) => { + if (file.truncated) { + getGitHubJSON(file.raw_url, this.accessToken) + .then((content) => { + resolve({ + file: filename, + content: content + }); + }) + .fail(reject) + } else { + resolve({ + file: filename, + content: file.content + }); + } + }); }); + return Promise.all(promises); }); - return Promise.all(promises); - }); } getRepos() { return getAllItems( - Promise.resolve({ items: [], url: `${this.baseUrl}/user/repos?access_token=${this.accessToken}` }), - this.followPaginate, - 'github' - ) - .then(response => { - const repos = response.map(repo => repo.full_name); - //if current bind still existed, use it - const repo = context.bindRepo[context.id]; - if (repo && $.inArray(repo.fullName, repos) >= 0) { - context.repo = repo; - } else if (context.gist) { - context.repo = { - fullName: 'gist', - gist: true + Promise.resolve({ + accessToken: this.accessToken, + items: [], + url: `${this.baseUrl}/user/repos` + }), + this.followPaginate, + 'github' + ) + .then(response => { + const repos = response.map(repo => repo.full_name); + //if current bind still existed, use it + const repo = context.bindRepo[context.id]; + if (repo && $.inArray(repo.fullName, repos) >= 0) { + context.repo = repo; + } else if (context.gist) { + context.repo = { + fullName: 'gist', + gist: true + } } - } - return repos; - }); + return repos; + }); } getNamespaces() { - return getAllItems(Promise.resolve( - { - token: this.accessToken, - items: [], - url: `${this.baseUrl}/user/orgs?access_token=${this.accessToken}` - }), - this.followPaginate, - 'github' - ) - .then(orgs => { - this.namespaces = [this.user].concat(orgs.map(org => org.login)); - return this.namespaces; - }) - .catch((err) => { - showAlert('Failed to get user info.', LEVEL_ERROR); - }); + return getAllItems( + Promise.resolve({ + accessToken: this.accessToken, + items: [], + url: `${this.baseUrl}/user/orgs` + }), + this.followPaginate, + 'github' + ) + .then(orgs => { + this.namespaces = [this.user].concat(orgs.map(org => org.login)); + return this.namespaces; + }) + .catch((err) => { + showAlert('Failed to get user info.', LEVEL_ERROR); + }); } createRepo() { @@ -312,156 +335,169 @@ class Github { const desc = $('#new-repo-desc').val(); const isPrivate = $('#new-repo-type').val() !== 'public'; const payload = { - name : name, - description : desc, - auto_init : true, + name: name, + description: desc, + auto_init: true, private: isPrivate } const path = owner === this.user ? '/user/repos' : `/orgs/${owner}/repos`; if (!name || name === '') return; return new Promise((resolve, reject) => { - $.ajax({ - url: `${this.baseUrl}${path}`, - headers: { - 'Authorization': `token ${this.accessToken}` - }, - method: 'POST', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) + $.ajax({ + url: `${this.baseUrl}${path}`, + headers: { + 'Authorization': `token ${this.accessToken}` + }, + method: 'POST', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }) + .then(resolve) + .fail(reject); }) - .then(resolve) - .fail(reject); - }) - .then(response => { - const repo = { - fullName : response.full_name - }; - context.repo = repo; - Object.assign(context.bindRepo, { [context.id] : repo }); - if (context.bindBranch[context.id]) { - delete context.bindBranch[context.id]; - } - chrome.storage.sync.set({ bindRepo: context.bindRepo }); - return response.full_name; - }) - .catch(err => { - throw new Error('Failed to create new repository.'); - }); + .then(response => { + const repo = { + fullName: response.full_name + }; + context.repo = repo; + Object.assign(context.bindRepo, { + [context.id]: repo + }); + if (context.bindBranch[context.id]) { + delete context.bindBranch[context.id]; + } + chrome.storage.sync.set({ + bindRepo: context.bindRepo + }); + return response.full_name; + }) + .catch(err => { + throw new Error('Failed to create new repository.'); + }); } createGist() { const desc = $('#new-gist-name').val(); const isPublic = $('#new-gist-public').val() !== 'secret'; const payload = { - 'description' : desc, + 'description': desc, 'public': isPublic, 'files': { - 'init_by_gas_hub.html' : { + 'init_by_gas_hub.html': { 'content': 'init by gas-hub, just delete this file.' } } }; return new Promise((resolve, reject) => { - $.ajax({ - url: `${this.baseUrl}/gists`, - headers: { - 'Authorization': `token ${this.accessToken}` - }, - method: 'POST', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) + $.ajax({ + url: `${this.baseUrl}/gists`, + headers: { + 'Authorization': `token ${this.accessToken}` + }, + method: 'POST', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }) + .then(resolve) + .fail(reject); }) - .then(resolve) - .fail(reject); - }) - .then(response => { - const gist = response.id; - context.branch = gist; - Object.assign(context.bindBranch, { [context.id] : gist }); - chrome.storage.sync.set({ bindBranch: context.bindBranch }); - return response; - }) - .catch(err => { - throw new Error('Failed to create new gist.'); - }); + .then(response => { + const gist = response.id; + context.branch = gist; + Object.assign(context.bindBranch, { + [context.id]: gist + }); + chrome.storage.sync.set({ + bindBranch: context.bindBranch + }); + return response; + }) + .catch(err => { + throw new Error('Failed to create new gist.'); + }); } createBranch() { const branch = $('#new-branch-name').val(); if (!branch || branch === '') return; return new Promise((resolve, reject) => { - $.getJSON( - `${this.baseUrl}/repos/${context.repo.fullName}/git/refs/heads/${context.branch}`, - { access_token: this.accessToken } - ) - .then(resolve) - .fail(reject) - }) - .then(response => { - if (response.object) { - return response.object.sha; - } - else { - return $.getJSON( - `${this.baseUrl}/repos/${context.repo.fullName}/git/refs/heads`, - { access_token: this.accessToken } - ) - .then(response => { - return response[0].object.sha; - }) - } - }) - .then(sha => { - const payload = { - ref: `refs/heads/${branch}`, - sha: sha - }; - return $.ajax({ - url: `${this.baseUrl}/repos/${context.repo.fullName}/git/refs`, - headers: { - 'Authorization': `token ${this.accessToken}` - }, - method: 'POST', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) + getGitHubJSON( + `${this.baseUrl}/repos/${context.repo.fullName}/git/refs/heads/${context.branch}`, + this.accessToken) + .then(resolve) + .fail(reject) + }) + .then(response => { + if (response.object) { + return response.object.sha; + } else { + return getGitHubJSON( + `${this.baseUrl}/repos/${context.repo.fullName}/git/refs/heads`, + this.accessToken) + .then(response => { + return response[0].object.sha; + }) + } + }) + .then(sha => { + const payload = { + ref: `refs/heads/${branch}`, + sha: sha + }; + return $.ajax({ + url: `${this.baseUrl}/repos/${context.repo.fullName}/git/refs`, + headers: { + 'Authorization': `token ${this.accessToken}` + }, + method: 'POST', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }); + }) + .then(response => { + context.branch = branch; + Object.assign(context.bindBranch, { + [context.id]: branch + }); + chrome.storage.sync.set({ + bindBranch: context.bindBranch + }); + return branch; + }) + .catch(err => { + if (err.status === 409) { + throw new Error('Cannot create branch in empty repository with API, try to create branch in Github.'); + } else { + throw new Error('Failed to create new branch.'); + } }); - }) - .then(response => { - context.branch = branch; - Object.assign(context.bindBranch, { [context.id] : branch }); - chrome.storage.sync.set({ bindBranch: context.bindBranch }); - return branch; - }) - .catch(err => { - if (err.status === 409) { - throw new Error('Cannot create branch in empty repository with API, try to create branch in Github.'); - } else { - throw new Error('Failed to create new branch.'); - } - }); } - + followPaginate(data) { return new Promise((resolve, reject) => { - $.getJSON(data.url) - .then((response, status, xhr) => { - data.items = data.items.concat(response); - const link = xhr.getResponseHeader('Link'); - let url = null; - if (link) { - const match = link.match(/<(.*?)>; rel="next"/); - url = match && match[1] ? match[1] : null; - } - resolve({ items: data.items, url: url }); - }) - .fail(reject); + getGitHubJSON(data.url, data.accessToken) + .then((response, status, xhr) => { + data.items = data.items.concat(response); + const link = xhr.getResponseHeader('Link'); + let url = null; + if (link) { + const match = link.match(/.*<(.*?)>; rel="next"/); + url = match && match[1] ? match[1] : null; + } + resolve({ + items: data.items, + url: url, + accessToken: data.accessToken + }); + }) + .fail(reject); }); } } \ No newline at end of file diff --git a/src/scm/gitlab.js b/src/scm/gitlab.js index a549490..bb7eefe 100644 --- a/src/scm/gitlab.js +++ b/src/scm/gitlab.js @@ -9,7 +9,7 @@ class Gitlab { groups: {} }; this.accessToken = token.token; - if(token.type === 'oAuth') { + if (token.type === 'oAuth') { this.tokenParam = `access_token=${this.accessToken}`; this.tokenHeader = { 'Authorization': `Bearer ${this.accessToken}` @@ -33,7 +33,11 @@ class Gitlab { commitFiles(repo, branch, parent, newFiles, changedFiles, deleteFiles, comment) { return new Promise((resolve, reject) => { - const data = {branch: branch, commit_message: comment, actions: []}; + const data = { + branch: branch, + commit_message: comment, + actions: [] + }; if (newFiles && newFiles.length > 0) { data.actions = data.actions.concat(newFiles.map((file) => { return { @@ -53,29 +57,34 @@ class Gitlab { })); } if (deleteFiles && deleteFiles.length > 0) { - data.actions = data.actions.concat(deleteFiles.map((file) => {return { - action : 'delete', - file_path : file - }})); + data.actions = data.actions.concat(deleteFiles.map((file) => { + return { + action: 'delete', + file_path: file + } + })); } $.ajax({ - url: `${this.baseUrl}/projects/${context.repo.id}/repository/commits`, - headers: this.tokenHeader, - contentType: 'application/json', - method: 'POST', - crossDomain: true, - traditional: true, - data: JSON.stringify(data) - }) - .then(resolve) - .fail(reject); + url: `${this.baseUrl}/projects/${context.repo.id}/repository/commits`, + headers: this.tokenHeader, + contentType: 'application/json', + method: 'POST', + crossDomain: true, + traditional: true, + data: JSON.stringify(data) + }) + .then(resolve) + .fail(reject); }); } push(code) { const changed = $('.diff-file:checked').toArray().map(elem => elem.value); const changedFiles = changed.filter(f => code.gas[f]).map(f => { - return {name: f, content: code.gas[f]} + return { + name: f, + content: code.gas[f] + } }); const deleteFiles = changed.filter(f => !code.gas[f]); const newFileNames = changed.filter(f => !code.scm[f]); @@ -91,14 +100,13 @@ class Gitlab { showAlert(`Successfully push to ${context.branch} of ${context.repo.fullName}`); }) .catch((err) => { - showAlert('Failed to push', LEVEL_ERROR); + showAlert(`Failed to push: ${err}`, LEVEL_ERROR); }); } getAllBranches() { context.repo.id = context.repo.id || this.namesToIds.repos[context.repo.fullName]; - return getAllItems(Promise.resolve( - { + return getAllItems(Promise.resolve({ tokenParam: this.tokenParam, items: [], url: `${this.baseUrl}/projects/${context.repo.id}/repository/branches?per_page=25` @@ -110,70 +118,71 @@ class Gitlab { getCode() { return new Promise((resolve, reject) => { - return $.getJSON( - `${this.baseUrl}/projects/${context.repo.id}/repository/tree?ref=${context.branch}&recursive=true&${this.tokenParam}`, {} - ) - .then(resolve) - .fail(reject) - }) - .then(response => { - const re = new RegExp(`(\\${context.config.filetype}|\\.html${context.config.manifestEnabled ? '|^appsscript.json' : ''})$`); - const promises = response.filter((tree) => { - return tree.type === 'blob' && re.test(tree.path); + return $.getJSON( + `${this.baseUrl}/projects/${context.repo.id}/repository/tree?ref=${context.branch}&recursive=true&${this.tokenParam}`, {} + ) + .then(resolve) + .fail(reject) }) - .map(tree => { - return new Promise((resolve, reject) => { - $.getJSON(`${this.baseUrl}/projects/${context.repo.id}/repository/files/${encodeURIComponent(tree.path)}?ref=${context.branch}&${this.tokenParam}`, {}) - .then((content) => { - resolve({file: tree.path, content: decodeURIComponent(escape(atob(content.content)))}); + .then(response => { + const re = new RegExp(`(\\${context.config.filetype}|\\.html${context.config.manifestEnabled ? '|^appsscript.json' : ''})$`); + const promises = response.filter((tree) => { + return tree.type === 'blob' && re.test(tree.path); }) - .fail(reject) - }); + .map(tree => { + return new Promise((resolve, reject) => { + $.getJSON(`${this.baseUrl}/projects/${context.repo.id}/repository/files/${encodeURIComponent(tree.path)}?ref=${context.branch}&${this.tokenParam}`, {}) + .then((content) => { + resolve({ + file: tree.path, + content: decodeURIComponent(escape(atob(content.content))) + }); + }) + .fail(reject) + }); + }); + return Promise.all(promises); }); - return Promise.all(promises); - }); } getNamespaces() { - return getAllItems(Promise.resolve( - { - tokenParam: this.tokenParam, - items: [], - url: `${this.baseUrl}/groups?per_page=25` - }), - this.followPaginate, - 'gitlab' - ) - .then(groups => { - this.namespaces = [this.user].concat(groups.map(group => group.name)); - this.namesToIds.groups = groups.reduce((obj, item) => (obj[item.name] = item.id, obj), {}); - return this.namespaces; - }) - .catch((err) => { - showAlert('Failed to get user info.', LEVEL_ERROR); - }); + return getAllItems(Promise.resolve({ + tokenParam: this.tokenParam, + items: [], + url: `${this.baseUrl}/groups?per_page=25` + }), + this.followPaginate, + 'gitlab' + ) + .then(groups => { + this.namespaces = [this.user].concat(groups.map(group => group.name)); + this.namesToIds.groups = groups.reduce((obj, item) => (obj[item.name] = item.id, obj), {}); + return this.namespaces; + }) + .catch((err) => { + showAlert('Failed to get user info.', LEVEL_ERROR); + }); } getRepos() { // Named Projects in gitlab - return getAllItems(Promise.resolve( - { - tokenParam: this.tokenParam, - items: [], - url: `${this.baseUrl}/projects?per_page=25&membership=true` - }), - this.followPaginate, - 'gitlab' - ) - .then(response => { - this.namesToIds.repos = response.reduce((obj, item) => (obj[item.path_with_namespace] = item.id, obj), {}); - const repos = Object.keys(this.namesToIds.repos); - //if current bind still existed, use it - const repo = context.bindRepo[context.id]; - if (repo && $.inArray(repo.fullName, repos) >= 0) { - context.repo = repo; - } - return repos; - }); + return getAllItems(Promise.resolve({ + tokenParam: this.tokenParam, + items: [], + url: `${this.baseUrl}/projects?per_page=25&membership=true` + }), + this.followPaginate, + 'gitlab' + ) + .then(response => { + this.namesToIds.repos = response.reduce((obj, item) => (obj[item.path_with_namespace] = item.id, obj), {}); + const repos = Object.keys(this.namesToIds.repos); + //if current bind still existed, use it + const repo = context.bindRepo[context.id]; + if (repo && $.inArray(repo.fullName, repos) >= 0) { + context.repo = repo; + } + return repos; + }); } createRepo() { @@ -191,40 +200,47 @@ class Gitlab { } if (!name || name === '') return; return new Promise((resolve, reject) => { - return $.ajax({ - url: `${this.baseUrl}/projects`, - headers: this.tokenHeader, - method: 'POST', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) + return $.ajax({ + url: `${this.baseUrl}/projects`, + headers: this.tokenHeader, + method: 'POST', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }) + .then(resolve) + .fail(reject); }) - .then(resolve) - .fail(reject); - }) - .then(response => { - const repo = { - fullName: response.path_with_namespace, - id: response.id - }; - context.repo = repo; - Object.assign(context.bindRepo, {[context.id]: repo}); - if (context.bindBranch[context.id]) { - delete context.bindBranch[context.id]; - } - chrome.storage.sync.set({bindRepo: context.bindRepo}); - return response.path_with_namespace; - }) - .then(repo => { - return this.commitFiles(repo, 'master', null, [{name: "README.md", content: "initialed by gas-github"}], null,null, 'initial commit') - .then(() => { - return repo; + .then(response => { + const repo = { + fullName: response.path_with_namespace, + id: response.id + }; + context.repo = repo; + Object.assign(context.bindRepo, { + [context.id]: repo + }); + if (context.bindBranch[context.id]) { + delete context.bindBranch[context.id]; + } + chrome.storage.sync.set({ + bindRepo: context.bindRepo }); + return response.path_with_namespace; }) - .catch((err) => { - throw new Error('Failed to create new repository.'); - }); + .then(repo => { + return this.commitFiles(repo, 'master', null, [{ + name: "README.md", + content: "initialed by gas-github" + }], null, null, 'initial commit') + .then(() => { + return repo; + }); + }) + .catch((err) => { + throw new Error(`Failed to create new repository: ${err}`); + }); } createBranch() { @@ -236,23 +252,27 @@ class Gitlab { if (!branch || branch === '') return; return new Promise((resolve, reject) => { return $.ajax({ - url: `${this.baseUrl}/projects/${context.repo.id}/repository/branches`, - headers: this.tokenHeader, - method: 'POST', - crossDomain: true, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload) - }) + url: `${this.baseUrl}/projects/${context.repo.id}/repository/branches`, + headers: this.tokenHeader, + method: 'POST', + crossDomain: true, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload) + }) .then(resolve) .fail(reject); }) - .then(response => { - context.branch = branch; - Object.assign(context.bindBranch, { [context.id] : branch }); - chrome.storage.sync.set({ bindBranch: context.bindBranch }); - return branch; - }) + .then(response => { + context.branch = branch; + Object.assign(context.bindBranch, { + [context.id]: branch + }); + chrome.storage.sync.set({ + bindBranch: context.bindBranch + }); + return branch; + }) .catch(err => { throw new Error('Failed to create new branch.'); }); @@ -269,9 +289,12 @@ class Gitlab { const match = link.match(/<([^ ]*?)>; rel="next"/); url = match && match[1] ? match[1] : null; } - resolve({ items: data.items, url: url }); + resolve({ + items: data.items, + url: url + }); }) .fail(reject); }) } -} +} \ No newline at end of file diff --git a/src/util.js b/src/util.js index 76b6758..4f61a66 100644 --- a/src/util.js +++ b/src/util.js @@ -18,4 +18,18 @@ function createSCM(item) { default: return new Github(item.baseUrl, item.user, item.token); } +} + +function getGitHubJSON(url, accessToken, data) { + return $.ajax({ + url: url, + headers: { + 'Authorization': `token ${accessToken}` + }, + method: 'GET', + crossDomain: true, + dataType: 'json', + data: data, + contentType: 'application/json' + }) } \ No newline at end of file From 12f2f409253a51f1e52f443638851bb3c0ca7ab4 Mon Sep 17 00:00:00 2001 From: leonhartX Date: Sat, 8 Feb 2020 17:47:11 +0900 Subject: [PATCH 4/4] add more erro detail --- src/scm/github.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scm/github.js b/src/scm/github.js index 7142a4f..9288e93 100644 --- a/src/scm/github.js +++ b/src/scm/github.js @@ -144,7 +144,7 @@ class Github { showAlert(`Successfully push to ${context.branch} of ${context.repo.fullName}`); }) .catch(err => { - showAlert('Failed to push', LEVEL_ERROR); + showAlert(`Failed to push: ${err}`, LEVEL_ERROR); }); } @@ -325,7 +325,7 @@ class Github { return this.namespaces; }) .catch((err) => { - showAlert('Failed to get user info.', LEVEL_ERROR); + showAlert(`Failed to get user info: ${err}`, LEVEL_ERROR); }); }