ring-websites-toolbelt
Version:
Ring Publishing Platform tool to work with Ring Websites
343 lines (296 loc) • 14 kB
JavaScript
const fs = require('fs');
const prompts = require('prompts');
const homedir = require('os').homedir();
const ThemeScriptAbstract = require(`./../../ThemeScriptAbstract`);
const ExternalApiProvider = require('../../providers/ExternalApiProvider');
class Setup extends ThemeScriptAbstract {
constructor(options) {
super(options);
this.ucsConfig = {};
this.themesDeployed = [];
}
async execute() {
this.validateThemeJson();
console.info('Setting up environment varriables');
await this.loadExistingConfig();
await this.chooseNamespace();
await this.setupConnectionParams();
await this.deployThemeIfNotYetDeployed();
await this.selectUcsVariant();
await this.setupS3Varriables();
await this.saveNamespaceConfigLocally();
console.info('Environment varriables successfuly set up! Use "website install" to download modules of your theme.');
}
async loadExistingConfig() {
if (fs.existsSync(this.paths.config)) {
console.info('This theme has already been set up in your environment. Confirm or edit required variables:');
try {
this.ucsConfig = JSON.parse(fs.readFileSync(this.paths.config, 'utf8'));
} catch (ex) {
throw new Error(`Invalid ${this.paths.config} content. Cannot parse file.`);
}
/**
* START: TO REMOVE --- only to support backward compatibility with 1.6.x
*/
if (this.ucsConfig && Object.keys(this.ucsConfig).length && !this.ucsConfig['___all_websites_configs']) {
const allWebsitesConfig = {};
Object.keys(this.ucsConfig).forEach((themeName) => {
allWebsitesConfig[this.ucsConfig[themeName].namespaceId] = this.ucsConfig[themeName];
});
this.ucsConfig['___all_websites_configs'] = allWebsitesConfig;
this.saveConfig();
}
/**
* END: TO REMOVE
*/
}
}
async chooseNamespace() {
const validate = value => !!value;
const defaultConfiguration = this.ucsConfig[this.themeJson.theme] || {};
const allNamespaces = this.ucsConfig['___all_websites_configs'] || {};
let typeNamespaceIdManually = true;
if (Object.keys(allNamespaces).length) {
const getNamespacesInfoPromisesArr = Object.keys(allNamespaces).map(namespaceId => {
const simpleConfig = {namespaceId, publicKey: allNamespaces[namespaceId].publicKey,
secretKey: allNamespaces[namespaceId].secretKey};
return (new ExternalApiProvider({}, simpleConfig)).getNamespace();
});
let namespacesInfo = [];
try {
namespacesInfo = await Promise.all(getNamespacesInfoPromisesArr);
} catch (err) {
console.error('Cannot load the names of the namespaces');
}
const selectChoices = Object.keys(allNamespaces).map(namespaceId => {
const namespaceData = namespacesInfo.filter(namespace => { return namespace.id === namespaceId })[0];
const title = namespaceData ? `${namespaceId} - ${namespaceData.name}` : namespaceId;
return {
title: title,
value: namespaceId
}
});
selectChoices.push({ title: '[New namespace configuration]', value: '[new]' });
const currentNamespace = selectChoices.findIndex(choice => choice.value === defaultConfiguration.namespaceId);
const selectedConfiguration = await prompts([{
type: 'select',
name: 'namespaceId',
message: 'What is namespaceId of your Ring Website? [namespaceId]',
choices: selectChoices,
initial: currentNamespace !== -1 ? currentNamespace : 0
}]);
if (selectedConfiguration.namespaceId) {
if (selectedConfiguration.namespaceId !== '[new]') {
typeNamespaceIdManually = false;
this.ucsConfig[this.themeJson.theme] = {
...this.ucsConfig[this.themeJson.theme],
...this.ucsConfig['___all_websites_configs'][selectedConfiguration.namespaceId]
};
}
} else {
throw new Error(`Configuration is not fully set up. After you make required changes, please run "website setup" again.`);
}
}
if (typeNamespaceIdManually) {
const selectedConfiguration = await prompts([{
type: 'text',
name: 'namespaceId',
message: 'What is namespaceId of your Ring Website? [namespaceId]',
initial: '',
validate
}]);
if (selectedConfiguration.namespaceId) {
if (!this.ucsConfig[this.themeJson.theme]) {
this.ucsConfig[this.themeJson.theme] = {};
}
this.ucsConfig[this.themeJson.theme].namespaceId = selectedConfiguration.namespaceId;
this.ucsConfig[this.themeJson.theme].publicKey = '';
this.ucsConfig[this.themeJson.theme].secretKey = '';
} else {
throw new Error(`Configuration is not fully set up. After you make required changes, please run "website setup" again.`);
}
}
}
async setupConnectionParams() {
const validate = value => !!value;
const defaultConfiguration = this.ucsConfig[this.themeJson.theme] || {};
const selectedConfiguration = await prompts([
{
type: 'text',
name: 'publicKey',
message: 'What is your public key to Ring Websites API of that namespace? [publicKey]',
initial: defaultConfiguration.publicKey || '',
validate
},
{
type: 'text',
name: 'secretKey',
message: 'What is the secret key to that private key? [secretKey]',
initial: defaultConfiguration.secretKey || '',
validate
}
]);
if (selectedConfiguration.publicKey && selectedConfiguration.secretKey) {
this.ucsConfig[this.themeJson.theme] = {
...this.ucsConfig[this.themeJson.theme],
...selectedConfiguration
};
} else {
throw new Error(`Configuration is not fully set up. After you make required changes, please run "website setup" again.`);
}
}
async deployThemeIfNotYetDeployed() {
let namespaceThemes;
this.config = this.ucsConfig[this.themeJson.theme];
this.setUpProviders();
try {
namespaceThemes = await this.api.getThemes();
} catch (error) {
if (error.code === 403) {
throw new Error(`Authorization failed. Check validity of selected namespace and its authorization keys.`);
} else {
throw error;
}
}
// save config up to this point (namespaceId, publicKey and secretKey) as they are valid (request above succeded)
this.saveConfig();
this.themesDeployed = namespaceThemes
.filter(theme => theme.theme === this.themeJson.theme)
.map(theme => theme.version);
if (!this.themesDeployed.length) {
console.warn(`Your theme is currently not deployed in any version.`);
console.warn(`In order to create a development variant your theme has to be deployed at least once.`);
const emptyThemeDeploy = await prompts([{
type: 'toggle',
name: 'accept',
message: `Do you want to deploy your theme "${this.themeJson.theme}" with version "0.0.0" and empty configuration?`,
initial: true,
active: 'yes',
inactive: 'no'
}]);
if (emptyThemeDeploy.accept) {
await this.api.deployEmptyTheme();
this.themesDeployed.push('0.0.0');
console.info(`Theme successfuly deployed: ${this.themeJson.theme}@0.0.0`);
} else {
throw new Error(`Configuration is not fully set up. After you make required changes, please run "website setup" again.`);
}
}
}
async selectUcsVariant() {
const themeDevVariants = (await this.api.getVariants()).filter(variant => variant.theme === this.themeJson.theme && variant.dev_mode_enabled);
const selectChoices = themeDevVariants.map(variant => ({ title: variant.variant_name, value: variant.variant_name }));
selectChoices.push({ title: '[Create new variant]', value: '[new]' });
const validate = value => !!value;
const defaultConfiguration = this.ucsConfig[this.themeJson.theme] || {};
const currentVariant = selectChoices.findIndex(choice => choice.value === defaultConfiguration.ucsVariant);
const selectedConfiguration = await prompts([
{
type: 'select',
name: 'ucsVariant',
message: 'Select Ring Website development variant you are going to develop on',
choices: selectChoices,
initial: currentVariant !== -1 ? currentVariant : 0
}
]);
if (selectedConfiguration.ucsVariant) {
if (selectedConfiguration.ucsVariant === '[new]') {
await this.createNewVariant();
} else {
this.ucsConfig[this.themeJson.theme] = {
...this.ucsConfig[this.themeJson.theme],
...selectedConfiguration
};
}
// save config up to this point (ucsVariant) as it was created here or selected
this.saveConfig();
} else {
throw new Error(`Configuration is not fully set up. After you make required changes, please run "website setup" again.`);
}
}
async createNewVariant() {
const validate = value => !!value;
const themeVersionPrompt = this.themesDeployed.length <= 1 ? [] : [{
type: 'select',
name: 'themeVersion',
message: 'Select theme version for new variant',
choices: this.themesDeployed.map(theme => ({ title: theme, value: theme })),
initial: 0
}];
const selectedConfiguration = await prompts([
{
type: 'text',
name: 'ucsVariant',
message: 'What will be the name of your variant?',
initial: '',
validate
},
...themeVersionPrompt
]);
await this.api.createVariant({
variantName: selectedConfiguration.ucsVariant,
version: this.themesDeployed.length <= 1 ? this.themesDeployed[0] : selectedConfiguration.themeVersion
});
await this.api.updateVariant({
ucsVariant: selectedConfiguration.ucsVariant,
devModeEnabled: true
});
this.ucsConfig[this.themeJson.theme] = {
...this.ucsConfig[this.themeJson.theme],
...selectedConfiguration
};
}
async setupS3Varriables() {
const validate = value => !!value;
const defaultConfiguration = this.ucsConfig[this.themeJson.theme] || {};
const selectedConfiguration = await prompts([
{
type: 'text',
name: 's3BucketName',
message: 'What is your S3 bucket name where we can store your static files? [s3BucketName]',
initial: defaultConfiguration.s3BucketName || '',
validate
},
{
type: 'text',
name: 's3AccessKey',
message: 'What is the access key to that bucket? [s3AccessKey]',
initial: defaultConfiguration.s3AccessKey || '',
validate
},
{
type: 'text',
name: 's3SecretKey',
message: 'What is the secret key to that access key? [s3SecretKey]',
initial: defaultConfiguration.s3SecretKey || '',
validate
}
]);
if (selectedConfiguration.s3BucketName &&
selectedConfiguration.s3AccessKey &&
selectedConfiguration.s3SecretKey) {
this.ucsConfig[this.themeJson.theme] = {
...this.ucsConfig[this.themeJson.theme],
...selectedConfiguration
};
this.saveConfig();
} else {
throw new Error(`Configuration is not fully set up. After you make required changes, please run "website setup" again.`);
}
}
async saveNamespaceConfigLocally() {
const themeConfig = this.ucsConfig[this.themeJson.theme];
this.ucsConfig['___all_websites_configs'] = {
...this.ucsConfig['___all_websites_configs'],
[themeConfig.namespaceId]: themeConfig
};
this.saveConfig();
}
saveConfig() {
if (fs.existsSync(this.paths.config)) {
fs.unlinkSync(this.paths.config);
}
fs.appendFileSync(this.paths.config, JSON.stringify(this.ucsConfig), 'utf8');
}
}
module.exports = Setup;