From a6e0f241e2c51bd3b7aa548b1244319f5aa9a78e Mon Sep 17 00:00:00 2001 From: Wade Wegner Date: Thu, 27 Jul 2017 20:11:58 -0700 Subject: [PATCH] Lots of little improvements --- app.js | 241 +++++++++++++++++++++++++------------- lib/commands.js | 15 +++ scripts/deploying.js | 54 +++++---- views/pages/deploy.ejs | 2 +- views/pages/notdevhub.ejs | 41 +++++++ 5 files changed, 248 insertions(+), 105 deletions(-) create mode 100644 lib/commands.js create mode 100644 views/pages/notdevhub.ejs diff --git a/app.js b/app.js index 9ba69d6..15b3bf3 100644 --- a/app.js +++ b/app.js @@ -1,18 +1,21 @@ const express = require('express'); -const app = express(); const bodyParser = require('body-parser'); -const sleep = require('sleep'); -var cookieParser = require('cookie-parser') +const cookieParser = require('cookie-parser'); +const oauth2 = require('salesforce-oauth2'); +const jsforce = require('jsforce'); +const commands = require('./lib/commands.js'); + const { exec } = require('child_process'); -const oauth2 = require('salesforce-oauth2'); + +const app = express(); const callbackUrl = process.env.CALLBACKURL; const consumerKey = process.env.CONSUMERKEY; const consumerSecret = process.env.CONSUMERSECRET; -app.use('/scripts', express.static(__dirname + '/scripts')); +app.use('/scripts', express.static(`${__dirname}/scripts`)); app.use(bodyParser.urlencoded({ extended: true @@ -22,15 +25,27 @@ app.set('view engine', 'ejs'); app.use(cookieParser()); -app.get('/', function (req, res) { +app.get('/', (req, res) => { res.render('pages/index', {}); }); -app.get('/about', function (req, res) { +app.get('/about', (req, res) => { res.render('pages/about'); }); -app.get('/deploy', function (req, res) { +app.get('/notdevhub', (req, res) => { + const template = req.query.template; + res.cookie('template', template); + + const user_name = req.cookies.user_name; + + res.render('pages/notdevhub', { + template: template, + user_name: user_name + }); +}); + +app.get('/deploy', (req, res) => { const template = req.query.template; res.cookie('template', template); @@ -51,145 +66,207 @@ app.get('/deploy', function (req, res) { } }); -app.get('/deploying', function (req, res) { +app.get('/deploying', (req, res) => { const template = req.query.template; res.render('pages/deploying', { template: template }); }); -app.get("/login", function (req, res) { - var uri = oauth2.getAuthorizationUrl({ +app.get("/login", (req, res) => { + const uri = oauth2.getAuthorizationUrl({ redirect_uri: callbackUrl, client_id: consumerKey, scope: 'id api refresh_token openid', state: 'test123' - // You can change loginUrl to connect to sandbox or prerelease env. - //base_url: 'https://test.my.salesforce.com' }); return res.redirect(uri); }); -app.get('/logout', function (req, res) { - res.clearCookie('access_token'); - res.clearCookie('instance_url'); +app.get('/logout', (req, res) => { - return res.redirect('/'); + const access_token = req.cookies.access_token; + const instance_url = req.cookies.instance_url; + + const conn = new jsforce.Connection({ + instanceUrl: instance_url, + accessToken: access_token + }); + + conn.logout((err) => { + if (err) { + return console.error(err); + } + res.clearCookie('access_token'); + res.clearCookie('instance_url'); + + return res.redirect('/'); + }); }); -app.get('/oauth/callback', function (req, res) { +app.get('/oauth/callback', (req, res) => { const authorizationCode = req.param('code'); - const state = req.param('state'); - console.log('state', state); + // const state = req.param('state'); // TODO: use state and query instead of cookies oauth2.authenticate({ redirect_uri: callbackUrl, client_id: consumerKey, client_secret: consumerSecret, code: authorizationCode - }, function (error, payload) { + }, (error, payload) => { res.cookie('access_token', payload.access_token); res.cookie('instance_url', payload.instance_url); res.cookie('refresh_token', payload.refresh_token); - res.cookie('user_name', payload.id); console.log(payload); - const template = req.cookies.template; - return res.redirect(`/deploy?template=${template}`); + // check to see if org is a dev hub + const conn = new jsforce.Connection({ + instanceUrl: payload.instance_url, + accessToken: payload.access_token + }); + + conn.identity((err, identity) => { + if (err) { + return console.error(err); + } + + res.cookie('user_name', identity.username); + + conn.tooling.query("SELECT DurableId, SettingValue FROM OrganizationSettingsDetail WHERE SettingName = 'ScratchOrgManagementPref'", (err, result) => { + if (err) { + return console.error(err); + } + + const template = req.cookies.template; + + if (result.size > 0) { + const devHubEnabled = result.records[0].SettingValue; + + if (devHubEnabled === true) { + return res.redirect(`/deploy?template=${template}`); + } else { + return res.redirect(`/notdevhub?template=${template}`); + } + } else { + return res.redirect(`/notdevhub?template=${template}`); + } + }); + }); }); }); -var router = express.Router(); +const router = express.Router(); -router.post('/deploying', function (req, res) { - - var command = req.body.command; - var param = req.body.param; +router.post('/deploying', (req, res) => { + const command = req.body.command; + const timestamp = req.body.timestamp; + const param = req.body.param; const access_token = req.cookies.access_token; const instance_url = req.cookies.instance_url; const refresh_token = req.cookies.refresh_token; - if (command === 'clone') { + const tokenName = access_token.replace(/\W/g, ''); + const startingDirectory = process.env.STARTINGDIRECTORY; + const directory = `${tokenName}-${timestamp}`; - exec(`cd /tmp;mkdir test;cd test;git clone ${param} repo`, (err, stdout, stderr) => { - res.json({ - message: `Successfully cloned ${param}` + let script; + let sfdxurl; + + switch (command) { + + case 'clone': + + script = `${startingDirectory}mkdir ${directory};cd ${directory};git clone ${param} .`; + + commands.run(command, script, () => { + res.json({ + message: `Successfully cloned ${param}` + }); }); - }); - } + break; - if (command === 'auth') { + case 'auth': - const sfdxurl = `echo "force://${consumerKey}:${consumerSecret}:${refresh_token}@${instance_url}" > sfdx.key`; - const commandScript = `cd /tmp/test/repo;export FORCE_SHOW_SPINNER=;${sfdxurl};sfdx force:auth:sfdxurl:store -f sfdx.key -d`; - console.log('auth', commandScript); + sfdxurl = `echo "force://${consumerKey}:${consumerSecret}:${refresh_token}@${instance_url}" > sfdx.key`; + script = `${startingDirectory}cd ${directory};export FORCE_SHOW_SPINNER=;${sfdxurl};sfdx force:auth:sfdxurl:store -f sfdx.key -d`; - exec(commandScript, (err, stdout, stderr) => { - console.log('create:stderr', stderr); - res.json({ - message: `Authenticated to dev hub: ${stdout}` + commands.run(command, script, (result) => { + res.json({ + message: `Authenticated to dev hub: ${result}` + }); }); - }); - } - if (command === 'create') { + break; - const commandScript = `cd /tmp/test/repo;export FORCE_SHOW_SPINNER=;sfdx force:org:create -s -f ${param}`; - console.log('create', commandScript); + case 'create': - exec(commandScript, (err, stdout, stderr) => { - console.log('create:stderr', stderr); - res.json({ - message: `Created scratch org: ${stdout}` + script = `${startingDirectory}cd ${directory};export FORCE_SHOW_SPINNER=;sfdx force:config:set instanceUrl=${instance_url};sfdx force:org:create -v '${access_token}' -s -f ${param}`; + + commands.run(command, script, (result) => { + res.json({ + message: `Created scratch org: ${result}` + }); }); - }); - } - if (command === 'push') { - exec(`cd /tmp/test/repo;export FORCE_SHOW_SPINNER=;sfdx force:source:push`, (err, stdout, stderr) => { - res.json({ - message: `Pushed source:\n\t${stdout}` + break; + + case 'push': + + script = `${startingDirectory}cd ${directory};export FORCE_SHOW_SPINNER=;sfdx force:source:push`; + + commands.run(command, script, (result) => { + res.json({ + message: `Pushed source:\n\t${result}` + }); }); - }); - } - if (command === 'test') { - exec(`cd /tmp/test/repo;export FORCE_SHOW_SPINNER=;sfdx force:apex:test:run -r human --json | jq -r .result | jq -r .summary | jq -r .outcome`, (err, stdout, stderr) => { - res.json({ - message: `Apex tests: ${stdout}` + break; + + case 'test': + + script = `${startingDirectory}cd ${directory};export FORCE_SHOW_SPINNER=;sfdx force:apex:test:run -r human --json | jq -r .result | jq -r .summary | jq -r .outcome`; + + commands.run(command, script, (result) => { + res.json({ + message: `Apex tests: ${result}` + }); }); - }); - } - if (command === 'url') { - const commandScript = 'cd /tmp/test/repo;export FORCE_SHOW_SPINNER=;echo $(sfdx force:org:display --json | jq -r .result | jq -r .instanceUrl)"/secur/frontdoor.jsp?sid="$(sfdx force:org:display --json | jq -r .result | jq -r .accessToken)'; + break; - exec(commandScript, (err, stdout, stderr) => { - res.json({ - message: `${stdout}` + case 'url': + + script = `${startingDirectory}cd ${directory};export FORCE_SHOW_SPINNER=;echo $(sfdx force:org:display --json | jq -r .result | jq -r .instanceUrl)"/secur/frontdoor.jsp?sid="$(sfdx force:org:display --json | jq -r .result | jq -r .accessToken)`; + + commands.run(command, script, (result) => { + res.json({ + message: `${result}` + }); }); - }); - } - if (command === 'clean') { + break; - const commandScript = 'rm -rf /tmp/test'; - exec(commandScript, (err, stdout, stderr) => { - res.json({ - message: 'Removed temp files and cleaned up' + case 'clean': + + script = `${startingDirectory}rm -rf ${directory}`; + + commands.run(command, script, () => { + res.json({ + message: 'Removed temp files and cleaned up' + }); }); - }); - } + break; + } }); app.use('/api', router); -var port = process.env.PORT || 8080; -app.listen(port, function () { +const port = process.env.PORT || 8080; +app.listen(port, () => { console.log(`Example app listening on port ${port}!`); }); \ No newline at end of file diff --git a/lib/commands.js b/lib/commands.js new file mode 100644 index 0000000..dba1fb1 --- /dev/null +++ b/lib/commands.js @@ -0,0 +1,15 @@ +const { + exec +} = require('child_process'); + +module.exports = { + run: (command, commandScript, result) => { + exec(commandScript, (err, stdout, stderr) => { + if (stderr || err) { + console.log(`${command}:err`, err); + console.log(`${command}:stderr`, stderr); + } + result(stdout); + }); + } +}; \ No newline at end of file diff --git a/scripts/deploying.js b/scripts/deploying.js index 873db79..e2af0a1 100644 --- a/scripts/deploying.js +++ b/scripts/deploying.js @@ -1,4 +1,4 @@ -$(document).ready(function () { +$(document).ready(() => { let actionCount = 0; let message = ''; @@ -10,13 +10,14 @@ $(document).ready(function () { $('textarea#status').val(message); } - async function deployingApi(command, param) { + function deployingApi(command, timestamp, param) { - commandData = {}; + const commandData = {}; commandData.command = command; + commandData.timestamp = timestamp; commandData.param = param; - await $.ajax({ + return $.ajax({ type: 'POST', url: '/api/deploying', data: JSON.stringify(commandData), @@ -28,8 +29,7 @@ $(document).ready(function () { }); } - let githubRepo = $('input#template').val(); - + const githubRepo = $('input#template').val(); let yamlFile = githubRepo.replace('github.com', 'raw.githubusercontent.com'); yamlFile += '/master/.salesforcedx.yaml'; @@ -42,7 +42,9 @@ $(document).ready(function () { update_status(`Discovered ${yamlFile}`); - var doc = jsyaml.load(yamlFileDataResponse); + const doc = jsyaml.load(yamlFileDataResponse); + + const timestamp = new Date().getTime().toString(); const assignPermset = doc['assign-permset']; const deleteScratchOrg = doc['delete-scratch-org']; @@ -57,27 +59,25 @@ $(document).ready(function () { \tscratch-org-def: ${scratchOrgDef} \tshow-scratch-org-url: ${showScratchOrgUrl}`); - return deployingApi('clone', githubRepo) + return deployingApi('clone', timestamp, githubRepo) .then(() => { - return deployingApi('auth'); + return deployingApi('auth', timestamp); }) .then(() => { - return deployingApi('create', scratchOrgDef); + return deployingApi('create', timestamp, scratchOrgDef); }) .then(() => { - return deployingApi('push'); + return deployingApi('push', timestamp); }) .then(() => { - return deployingApi('auth'); - }) - .then(() => { - return deployingApi('test'); + return deployingApi('test', timestamp); }) .then(() => { // generate url - commandData = {}; + let commandData = {}; commandData.command = 'url'; + commandData.timestamp = timestamp; return $.ajax({ type: 'POST', @@ -86,23 +86,33 @@ $(document).ready(function () { contentType: 'application/json; charset=utf-8', dataType: 'json', success: (commandDataResponse) => { - update_status(`${commandDataResponse.message}`); + update_status(`Generated a login url: ${commandDataResponse.message}`); const url = commandDataResponse.message; - $("#loginUrl").attr("href", url); - $("#loginUrl").text(`${url.substring(0, 80)}...`); + $('#loginUrl').attr('href', url); + $('#loginUrl').text(`${url.substring(0, 80)}...`); $('#loginBlock').show(); // clean up commandData = {}; commandData.command = 'clean'; + commandData.timestamp = timestamp; + + // return deployingApi('clean', timestamp).then(() => { + + // }; - deployingApi(commandData); - message = `DONE!\n\n${message}`; - $('textarea#status').val(message); } + }).then(() => { + return deployingApi('clean', timestamp) + .then(() => { + + message = `Finished. You have deploy the app to Salesforce DX!\n\n${message}`; + $('textarea#status').val(message); + + }); }); }); } diff --git a/views/pages/deploy.ejs b/views/pages/deploy.ejs index 9eed531..4f17d65 100644 --- a/views/pages/deploy.ejs +++ b/views/pages/deploy.ejs @@ -14,7 +14,7 @@

Deploy

-

Hello, <%= user_name %>! You've indicated you'd like to deploy: <%= template %>. Is that correct?

+

Hello, <%= user_name %>! You've indicated you'd like to deploy: <%= template %>. Is that correct?

diff --git a/views/pages/notdevhub.ejs b/views/pages/notdevhub.ejs new file mode 100644 index 0000000..819e135 --- /dev/null +++ b/views/pages/notdevhub.ejs @@ -0,0 +1,41 @@ + + + + + <% include ../partials/head %> + + + + +
+ <% include ../partials/header %> +
+ +
+
+

Not DevHub

+ +

+ Sorry, but it looks like the org you've logged into is not a dev hub. + Consequently, you cannot currently use Deploy to Salesforce DX to create a stratch org from a Github repo. +

+

To try this out, you have two options:

+
+
    +
  1. Enable dev hub in a Production or Business Org (i.e. not a DE org). Search on “Dev Hub” from Setup and then click Enabled.
  2. +
  3. Get a 30-day free trial.
  4. +
+
+

Please try again once you've gotten this resolved. +

+ +
+
+ + + + + + \ No newline at end of file