Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add locale and region for AHS #287

Merged
merged 6 commits into from
Aug 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions lib/clients/smapi-client/resources/alexa-hosted.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,21 @@ const NULL_PAYLOAD = null;
module.exports = (smapiHandle) => {
/**
* To submit a skill creation request for a specified vendorId
* @param {Object} manifest The JSON representation of the skill, and provides Alexa with all of the metadata required
* @param {Object} hostedSkillPayload The JSON representation of the skill, and provides Alexa with all of the metadata required
* @param {callback} callback { error, response }
*/
function createHostedSkill(manifest, callback) {
function createHostedSkill(hostedSkillPayload, callback) {
const url = 'skills/';
const runtime = CONSTANTS.HOSTED_SKILL.DEFAULT_RUNTIME[manifest.runtime];
const { vendorId, manifest, region } = hostedSkillPayload;
const runtime = CONSTANTS.HOSTED_SKILL.DEFAULT_RUNTIME[hostedSkillPayload.runtime];

const payload = {
vendorId: manifest.vendorId,
manifest: manifest.manifest,
vendorId,
manifest,
hosting: {
alexaHosted: {
runtime
runtime,
region
}
}
};
Expand Down
20 changes: 11 additions & 9 deletions lib/commands/new/hosted-skill-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,12 @@ function createHostedSkill(hostedSkillController, userInput, vendorId, callback)
if (fs.existsSync(projectPath)) {
return callback(`${projectPath} directory already exists.`);
}
const manifest = _updateManifest(userInput.skillName);
const manifest = _updateManifest(userInput);
const input = {
vendorId,
manifest,
runtime: userInput.language
runtime: userInput.language,
region: userInput.region
};

const listenSpinner = new SpinnerView();
Expand Down Expand Up @@ -108,16 +109,17 @@ function createHostedSkill(hostedSkillController, userInput, vendorId, callback)
}

/**
* To update hosted skill manifest template with customer input skill name
* To update hosted skill manifest template with user input
* and return the manifest object.
* @param {string} skillName The skill name
* @param {Object} userInput The skill name
*/
function _updateManifest(skillName) {
function _updateManifest(userInput) {
const { skillName, locale } = userInput;
const manifest = CONSTANTS.HOSTED_SKILL.MANIFEST;
const { locales } = manifest.publishingInformation;
Object.keys(locales).forEach((locale) => {
manifest.publishingInformation.locales[locale].name = skillName;
});

manifest.publishingInformation.locales[locale] = {};
manifest.publishingInformation.locales[locale].name = skillName;

return manifest;
}

Expand Down
29 changes: 29 additions & 0 deletions lib/commands/new/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const SKIP_DEPLOY_DELEGATE_SELECTION = 'self-hosted and manage your own hosting'

module.exports = {
getSkillName,
getSkillLocale,
getSkillDefaultRegion,
getProjectFolderName,
selectSkillCodeLanguage,
getTargetTemplateName,
Expand Down Expand Up @@ -39,6 +41,33 @@ function getSkillName(url, callback) {
});
}

function getSkillLocale(callback) {
inquirer.prompt([{
message: 'Choose the default locale for your skill: ',
type: 'list',
choices: CONSTANTS.HOSTED_SKILL.LOCALES,
name: 'locale',
pageSize: 5
}]).then((answer) => {
callback(null, answer.locale);
}).catch((error) => {
callback(error);
});
}

function getSkillDefaultRegion(callback) {
inquirer.prompt([{
message: 'Choose the default region for your skill: ',
type: 'list',
choices: Object.keys(CONSTANTS.HOSTED_SKILL.REGIONS),
name: 'region'
}]).then((answer) => {
callback(null, CONSTANTS.HOSTED_SKILL.REGIONS[answer.region]);
}).catch((error) => {
callback(error);
});
}

function getProjectFolderName(defaultName, callback) {
inquirer.prompt([{
message: 'Please type in your folder name for the skill project (alphanumeric): ',
Expand Down
61 changes: 44 additions & 17 deletions lib/commands/new/wizard-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,39 @@ function collectUserCreationProjectInfo(cmd, callback) {
return callback(deploymentErr);
}
userInput.deploymentType = deploymentType;
_getTemplateInfo(cmd, userInput, (templateErr, templateInfo) => {
if (templateErr) {
return callback(templateErr);
}
if (!templateInfo) {
return callback();

_getSkillLocale(userInput, (localeErr, locale) => {
if (localeErr) {
return callback(localeErr);
}
userInput.templateInfo = templateInfo;
ui.getSkillName(userInput.templateInfo.templateUrl, (getSkillNameErr, skillName) => {
if (getSkillNameErr) {
return callback(getSkillNameErr);
userInput.locale = locale;
_getSkillDefaultRegion(userInput, (regionErr, region) => {
if (regionErr) {
return callback(regionErr);
}
userInput.skillName = skillName;
const suggestedProjectName = stringUtils.filterNonAlphanumeric(skillName);
ui.getProjectFolderName(suggestedProjectName, (getFolderNameErr, folderName) => {
if (getFolderNameErr) {
return callback(getFolderNameErr);
userInput.region = region;
_getTemplateInfo(cmd, userInput, (templateErr, templateInfo) => {
if (templateErr) {
return callback(templateErr);
}
userInput.projectFolderName = folderName;
callback(null, userInput);
if (!templateInfo) {
return callback();
}
userInput.templateInfo = templateInfo;
ui.getSkillName(userInput.templateInfo.templateUrl, (getSkillNameErr, skillName) => {
if (getSkillNameErr) {
return callback(getSkillNameErr);
}
userInput.skillName = skillName;
const suggestedProjectName = stringUtils.filterNonAlphanumeric(skillName);
ui.getProjectFolderName(suggestedProjectName, (getFolderNameErr, folderName) => {
if (getFolderNameErr) {
return callback(getFolderNameErr);
}
userInput.projectFolderName = folderName;
callback(null, userInput);
});
});
});
});
});
Expand All @@ -68,6 +81,20 @@ function _getSkillCodeLanguage(cmd, callback) {
ui.selectSkillCodeLanguage((err, res) => callback(err || null, res));
}

function _getSkillLocale(userInput, callback) {
if (userInput.deploymentType !== CONSTANTS.DEPLOYER_TYPE.HOSTED.NAME) {
return callback();
}
ui.getSkillLocale((err, res) => callback(err || null, res));
}

function _getSkillDefaultRegion(userInput, callback) {
if (userInput.deploymentType !== CONSTANTS.DEPLOYER_TYPE.HOSTED.NAME) {
return callback();
}
ui.getSkillDefaultRegion((err, res) => callback(err || null, res));
}

/**
* Get template info for non-hosted skill
* @param {Object} cmd command object
Expand Down
6 changes: 3 additions & 3 deletions lib/controllers/hosted-skill-controller/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ module.exports = class HostedSkillController {

/**
* To create an Alexa hosted skill
* @param {JSON} manifest the JSON representation of the skill, and provides Alexa with all of the metadata required
* @param {JSON} hostedSkillPayload the JSON representation of the skill, and provides Alexa with all of the metadata required
* @param {callback} callback { error, response }
*/
createSkill(manifest, callback) {
this.smapiClient.skill.alexaHosted.createHostedSkill(manifest, (createErr, createRes) => {
createSkill(hostedSkillPayload, callback) {
this.smapiClient.skill.alexaHosted.createHostedSkill(hostedSkillPayload, (createErr, createRes) => {
if (createErr) {
return callback(createErr);
}
Expand Down
10 changes: 7 additions & 3 deletions lib/utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ module.exports.DEPLOY_TARGET = {

module.exports.HOSTED_SKILL = {
DEFAULT_SKILL_NAME: 'Hello World Skill',
LOCALES: ['de-DE', 'en-AU', 'en-CA', 'en-GB', 'en-IN', 'en-US', 'es-ES', 'es-MX',
'es-US', 'fr-CA', 'fr-FR', 'hi-IN', 'it-IT', 'ja-JP', 'pt-BR'],
REGIONS: {
'us-east-1': 'US_EAST_1',
'us-west-2': 'US_WEST_2',
'eu-west-1': 'EU_WEST_1'
},
DEFAULT_RUNTIME: {
NodeJS: 'NODE_10_X',
Python: 'PYTHON_3_7',
Expand All @@ -48,9 +55,6 @@ module.exports.HOSTED_SKILL = {
MANIFEST: {
publishingInformation: {
locales: {
'en-US': {
name: ''
}
}
},
apis: {
Expand Down
2 changes: 2 additions & 0 deletions test/functional/commands/high-level-commands-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ parallel('high level commands test', () => {
let inputs = [
{ match: '? Choose the programming language' },
{ match: '? Choose a method to host your skill' },
{ match: '? Choose the default locale for your skill' },
{ match: '? Choose the default region for your skill' },
{ match: '? Please type in your skill name' },
{ match: '? Please type in your folder name', input: folderName }
];
Expand Down
3 changes: 3 additions & 0 deletions test/unit/clients/smapi-client-test/resources/alexa-hosted.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ module.exports = (smapiClient) => {
const TEST_ACCESS_TOKEN = 'access_token';
const TEST_RUNTIME_FIELD = 'NodeJS';
const TEST_RUNTIME_VALUE = 'NODE_10_X';
const TEST_REGION_VALUE = 'US_EAST_1';
const TEST_MANIFEST = {
runtime: TEST_RUNTIME_FIELD,
vendorId: TEST_VENDOR_ID,
region: TEST_REGION_VALUE,
manifest: {}
};
let httpClientStub;
Expand All @@ -45,6 +47,7 @@ module.exports = (smapiClient) => {
manifest: {},
hosting: {
alexaHosted: {
region: TEST_REGION_VALUE,
runtime: TEST_RUNTIME_VALUE
}
}
Expand Down
69 changes: 69 additions & 0 deletions test/unit/commands/new/ui-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ function validateInquirerConfig(stub, expectedConfig) {

describe('Commands new - UI test', () => {
const TEST_SKILL_NAME = 'skillName';
const TEST_LOCALE = 'en-US';
const TEST_REPO_NAME = 'repo';
const TEST_FOLDER_NAME = 'folderName';
const TEST_URL = `https://${TEST_REPO_NAME}.git?data=1`;
Expand Down Expand Up @@ -144,6 +145,74 @@ describe('Commands new - UI test', () => {
});
});

describe('# validate ui.getSkillLocale', () => {
beforeEach(() => {
sinon.stub(inquirer, 'prompt');
});

afterEach(() => {
sinon.restore();
});

it('| getSkillLocale is set by user and inquirer throws exception', (done) => {
// setup
inquirer.prompt.rejects(new Error(TEST_ERROR));
// call
ui.getSkillLocale((err, response) => {
// verify
expect(err.message).equal(TEST_ERROR);
expect(response).equal(undefined);
done();
});
});

it('| getSkillLocale is set by user and return correctly', (done) => {
// setup
inquirer.prompt.resolves({ locale: TEST_LOCALE });
// call
ui.getSkillLocale((err, response) => {
// verify
expect(err).equal(null);
expect(response).equal(TEST_LOCALE);
done();
});
});
});

describe('# validate ui.getSkillDefaultRegion', () => {
beforeEach(() => {
sinon.stub(inquirer, 'prompt');
});

afterEach(() => {
sinon.restore();
});

it('| getSkillDefaultRegion is set by user and inquirer throws exception', (done) => {
// setup
inquirer.prompt.rejects(new Error(TEST_ERROR));
// call
ui.getSkillDefaultRegion((err, response) => {
// verify
expect(err.message).equal(TEST_ERROR);
expect(response).equal(undefined);
done();
});
});

it('| getSkillDefaultRegion is set by user and return correctly', (done) => {
// setup
inquirer.prompt.resolves({ region: 'us-east-1' });
// call
ui.getSkillDefaultRegion((err, response) => {
// verify
expect(err).equal(null);
expect(response).equal('US_EAST_1');
done();
});
});
});

describe('# validate ui.getProjectFolderName', () => {
beforeEach(() => {
sinon.stub(inquirer, 'prompt');
Expand Down
30 changes: 30 additions & 0 deletions test/unit/commands/new/wizard-helper-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ describe('Commands new test - wizard helper test', () => {
sinon.stub(ui, 'getTargetTemplateName');
sinon.stub(ui, 'getSkillName');
sinon.stub(ui, 'getProjectFolderName');
sinon.stub(ui, 'getSkillDefaultRegion');
sinon.stub(ui, 'getSkillLocale');
ui.getSkillLocale.yields(null, 'en-US');
ui.getSkillDefaultRegion.yields(0, null, 'US_EAST_1');
});

afterEach(() => {
Expand Down Expand Up @@ -85,6 +89,32 @@ describe('Commands new test - wizard helper test', () => {
});
});

it('| user input getSkillLocale fails, expect throw error', (done) => {
// setup
ui.selectSkillCodeLanguage.yields(null, TEST_LANGUAGE_RESPONSE);
ui.getDeploymentType.yields(null, TEST_HOSTED_DEPLOYMENT);
ui.getSkillLocale.yields(TEST_ERROR);
// call
wizardHelper.collectUserCreationProjectInfo(TEST_OPTIONS, (err) => {
// verify
expect(err).equal(TEST_ERROR);
done();
});
});

it('| user input getSkillDefaultRegion fails, expect throw error', (done) => {
// setup
ui.selectSkillCodeLanguage.yields(null, TEST_LANGUAGE_RESPONSE);
ui.getDeploymentType.yields(null, TEST_HOSTED_DEPLOYMENT);
ui.getSkillDefaultRegion.yields(TEST_ERROR);
// call
wizardHelper.collectUserCreationProjectInfo(TEST_OPTIONS, (err) => {
// verify
expect(err).equal(TEST_ERROR);
done();
});
});

it('| Hosted skills do not support Java, expect throw error', (done) => {
// setup
const TEST_HOSTED_ERROR = 'Alexa hosted skills don\'t support Java currently.';
Expand Down