Last active
February 7, 2021 04:17
-
-
Save goldzulu/5adae817b33533e43d0db6205caa257d to your computer and use it in GitHub Desktop.
node js executable script to change the lambda endpoints from command line E.g. Usage: node ./updateSkillEndpoint.js --url https://letmypeoplesleep.com ... or if you have ngrok running, can use node ./updateSkillEndpoint.js --ngrok ... you can optionally attached tunnelName to the --ngrok param if you have multiple tunnels.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env node | |
// node.js script to update the endpoint at the ADC to a local endpoint | |
// Originally written by @Voicetechguy1 twitch.tv/goldzulu | |
// This file is to be placed at the root of the Alexa Skill Project | |
// currently works only for ASK2 CLI | |
// Get the arguments from the command line and use the default below if none is given | |
// e.g. node ./updateSkillEndpoint.js --url https://letmypeoplesleep.com | |
// if you are using ngrok and ngrok is running before running this script | |
// the script can automatically detect the public url | |
// node ./updateSkillEndpoint.js --ngrok | |
// if you have more than one tunnel defined in ngrok config file | |
// node ./updateSkillEndpoint.js --ngrok <tunnelName> | |
let endpointUrl = "https://letmypeoplesleep.com"; | |
const win32flag = process.platform === "win32" ? true : false; | |
const pathSep = win32flag ? "\\" : "/"; | |
var myArgs = process.argv.slice(2); | |
// general function to validate url | |
function validURL(str) { | |
var pattern = new RegExp( | |
"^(https?:\\/\\/)?" + // protocol | |
"((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|" + // domain name | |
"((\\d{1,3}\\.){3}\\d{1,3}))" + // OR ip (v4) address | |
"(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" + // port and path | |
"(\\?[;&a-z\\d%_.~+=-]*)?" + // query string | |
"(\\#[-a-z\\d_]*)?$", | |
"i" | |
); // fragment locator | |
return !!pattern.test(str); | |
} | |
if (myArgs.length >= 2 && (myArgs[0] === "--url" || myArgs[0] === "-u")) { | |
if (validURL(myArgs[1])) { | |
endpointUrl = myArgs[1]; | |
} else { | |
console.error("Invalid https url."); | |
return false; | |
} | |
} | |
// Change the below to the skill local endpoint | |
// this would be the default used | |
// PART 1: Grab the SkillID | |
// use the get-skill-id.js file (now embedded in here) to get the skill id for the current alexa skill project | |
// check the blog at https://dzone.com/articles/syncing-local-alexa-skills-json-files-with-alexa-d | |
const fs = require("fs-sync"); | |
fs.defaultEncoding = "utf-8"; | |
const path = require("path"); | |
// The skill id extraction code is from a previous blog post and can extract both ASK1 | |
// and ASK2 structured skillid | |
// in theory doesnt matter as V1 is not supported in this script as is | |
const CONFIG_FILE = ".ask" + pathSep + "ask-states.json"; | |
const CONFIG_FILE_V1 = ".ask" + pathSep + "config"; | |
// Break the path into an array of folders and filter out | |
// the empty entry for the leading '/' | |
const folders = win32flag | |
? process.cwd().split(pathSep).filter(Boolean) | |
: process.env.PWD.split(pathSep).filter(Boolean); | |
// Locate the ASK Config file | |
const findAskConfigFile = async function (folders) { | |
// The ask cli downloads skills into a folder and | |
// writes the .ask/config or ..ask/ask-states.json (v2) there. There should | |
// never be one at your root. | |
if (folders.length <= 0) { | |
throw new Error("No config file found!"); | |
} | |
const directory = folders.join(pathSep); | |
const askConfigFile = win32flag | |
? path.join(directory, CONFIG_FILE) | |
: pathSep + path.join(directory, CONFIG_FILE); | |
const askConfigFileV1 = win32flag | |
? path.join(directory, CONFIG_FILE_V1) | |
: pathSep + path.join(directory, CONFIG_FILE_V1); | |
console.log("scanning... " + askConfigFile); | |
if (fs.exists(askConfigFile)) { | |
return await askConfigFile; | |
} else { | |
if (fs.exists(askConfigFileV1)) { | |
return await askConfigFileV1; | |
} | |
} | |
// if .ask/config or .ask/ask-states.json (v2) doesn't exist in the current directory | |
// look in the one above by removing the last item from | |
// folders and call findAskConfigFile again. Recursion. | |
folders.pop(); | |
return await findAskConfigFile(folders); | |
}; | |
// Get the skillId extracted from the skill config file | |
async function getSkillId() { | |
let configFile = await findAskConfigFile(folders); | |
if (configFile) { | |
let obj = fs.readJSON(configFile); | |
if (obj.profiles) { | |
return obj.profiles.default.skillId; | |
} else { | |
return obj.deploy_settings.default.skill_id; | |
} | |
} | |
} | |
// PART 2: Below is the code related to updating the actual endpoint | |
const exec = require("child_process").exec; | |
const currentSkillManifest = require("./skill-package/skill.json"); | |
// A functuon to execute a shell command and return back result via | |
// a callback | |
async function execute(command, callback) { | |
console.log("command: " + command); | |
await exec(command, function async(error, stdout, stderr) { | |
if (error) { | |
return Promise.reject("Error executing command"); | |
} | |
callback(error, stdout, stderr); | |
}); | |
} | |
function getCurrentSkillManifest(error, output, stderr) { | |
skill_manifest = JSON.parse(output); | |
let endpoint = skill_manifest.manifest.apis.custom.endpoint; | |
if (error) console.log("error: " + error); | |
else console.log("Previous endpoint was: " + JSON.stringify(endpoint)); | |
} | |
function updateSkillManifest(error, output, stderr) { | |
if (error) console.log("Update status: " + error); | |
else console.log("Update status: " + output); | |
} | |
async function main() { | |
let gskillid = await getSkillId(); | |
let command = win32flag | |
? `ask smapi get-skill-manifest -s ${gskillid} -g "development"` | |
: `ask smapi get-skill-manifest -s ${gskillid} -g 'development'`; | |
if (myArgs.length >= 1 && (myArgs[0] === "--ngrok" || myArgs[0] === "-n")) { | |
const fetch = require("node-fetch"); | |
let namedTunnel = "/command_line"; | |
if (myArgs.length >= 2) namedTunnel = "/" + myArgs[1]; | |
fetch("http://localhost:4040/api/tunnels" + namedTunnel) | |
.then((res) => res.json()) | |
.then((json) => { | |
console.log(json); | |
return json; | |
}) | |
.then((secureTunnel) => { | |
// execute the commands | |
execute(command, getCurrentSkillManifest); | |
console.log(secureTunnel); | |
console.log("Updating to new endpoint : " + secureTunnel.public_url); | |
return secureTunnel.public_url; | |
}) | |
.then((endpointUrl) => { | |
// create a new endpoint using the endpoint specified | |
// certificate is set to Wildcard as per most local debugging | |
// tutorials suggestions | |
const newLocalEndpoint = { | |
sslCertificateType: "Wildcard", | |
uri: `${endpointUrl}`, | |
}; | |
currentSkillManifest.manifest.apis.custom.endpoint = newLocalEndpoint; | |
let updateCommand = win32flag | |
? `ask smapi update-skill-manifest -s ${gskillid} -g "development" --manifest "${JSON.stringify( | |
currentSkillManifest | |
).replace(/"/g, '\\"')}"` | |
: `ask smapi update-skill-manifest -s ${gskillid} -g 'development' --manifest '${JSON.stringify( | |
currentSkillManifest | |
)}'`; | |
execute(updateCommand, updateSkillManifest); | |
}) | |
.catch((err) => { | |
if (err.code === "ECONNREFUSED") { | |
return console.error("Error: Looks like you're not running ngrok."); | |
} | |
console.error(err); | |
}); | |
} | |
} | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment