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(storefront): STRF-9258 Stencil Pull: Provide an activate option #739

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
12 changes: 5 additions & 7 deletions bin/stencil-pull.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ require('colors');

const { PACKAGE_INFO, API_HOST } = require('../constants');
const program = require('../lib/commander');
const stencilPull = require('../lib/stencil-pull');
const StencilPull = require('../lib/stencil-pull');
const { checkNodeVersion } = require('../lib/cliCommon');
const { printCliResultErrorAndExit } = require('../lib/cliCommon');

Expand All @@ -22,6 +22,7 @@ program
'specify the channel ID of the storefront to pull configuration from',
parseInt,
)
.option('-a, --activate [variationname]', 'specify the variation of the theme to activate')
.parse(process.argv);

checkNodeVersion();
Expand All @@ -32,11 +33,8 @@ const options = {
saveConfigName: cliOptions.filename,
channelId: cliOptions.channel_id,
saved: cliOptions.saved || false,
applyTheme: true, // fix to be compatible with stencil-push.utils
applyTheme: true,
activate: cliOptions.activate,
};

stencilPull(options, (err) => {
if (err) {
printCliResultErrorAndExit(err);
}
});
new StencilPull().run(options).catch(printCliResultErrorAndExit);
2 changes: 1 addition & 1 deletion bin/stencil-start.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ program
.version(PACKAGE_INFO.version)
.option('-o, --open', 'Automatically open default browser')
.option('-v, --variation [name]', 'Set which theme variation to use while developing')
.option('-c, --channelId [channelId]', 'Set the channel id for the storefront')
.option('-c, --channelId [channelId]', 'Set the channel id for the storefront', parseInt)
.option('--host [hostname]', 'specify the api host')
.option(
'--tunnel [name]',
Expand Down
225 changes: 206 additions & 19 deletions lib/stencil-pull.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,208 @@
const async = require('async');
const stencilPushUtils = require('./stencil-push.utils');
const stencilPullUtils = require('./stencil-pull.utils');

function stencilPull(options = {}, callback) {
async.waterfall(
[
async.constant(options),
stencilPushUtils.readStencilConfigFile,
stencilPushUtils.getStoreHash,
stencilPushUtils.getChannels,
stencilPushUtils.promptUserForChannel,
stencilPullUtils.getChannelActiveTheme,
stencilPullUtils.getThemeConfiguration,
stencilPullUtils.mergeThemeConfiguration,
],
callback,
);
const fsModule = require('fs');
const _ = require('lodash');
const StencilConfigManager = require('./StencilConfigManager');
const themeApiClientModule = require('./theme-api-client');
const stencilPushUtilsModule = require('./stencil-push.utils');
const fsUtilsModule = require('./utils/fsUtils');

require('colors');

class StencilPull {
constructor({
stencilConfigManager = new StencilConfigManager(),
themeApiClient = themeApiClientModule,
stencilPushUtils = stencilPushUtilsModule,
fsUtils = fsUtilsModule,
fs = fsModule,
} = {}) {
this._stencilConfigManager = stencilConfigManager;
this._themeApiClient = themeApiClient;
this._stencilPushUtils = stencilPushUtils;
this._fsUtils = fsUtils;
this._fs = fs;
}

/**
* @param {Object} cliOptions
*/
async run(cliOptions) {
const stencilConfig = await this._stencilConfigManager.read();
const storeHash = await this._themeApiClient.getStoreHash({
storeUrl: stencilConfig.normalStoreUrl,
});

let { channelId } = cliOptions;
if (!channelId) {
const channels = await this._themeApiClient.getStoreChannels({
accessToken: stencilConfig.accessToken,
apiHost: cliOptions.apiHost,
storeHash,
});

channelId = await this._stencilPushUtils.promptUserToSelectChannel(channels);
}

const activeTheme = await this.getActiveTheme({
accessToken: stencilConfig.accessToken,
apiHost: cliOptions.apiHost,
storeHash,
channelId,
});

console.log('ok'.green + ` -- Fetched theme details for channel ${channelId}`);

const variations = await this._themeApiClient.getVariationsByThemeId({
accessToken: stencilConfig.accessToken,
apiHost: cliOptions.apiHost,
themeId: activeTheme.active_theme_uuid,
storeHash,
});

const variationId = this._stencilPushUtils.getActivatedVariation(
variations,
cliOptions.activate,
);

const remoteThemeConfiguration = await this.getThemeConfiguration({
saved: cliOptions.saved,
activeTheme,
accessToken: stencilConfig.accessToken,
apiHost: cliOptions.apiHost,
storeHash,
variationId,
});

console.log(
'ok'.green + ` -- Fetched ${cliOptions.saved ? 'saved' : 'active'} configuration`,
);

await this.mergeThemeConfiguration({
variationId,
activate: cliOptions.activate,
remoteThemeConfiguration,
saveConfigName: cliOptions.saveConfigName,
});

return true;
}

/**
* @param {Object} options
* @param {String} options.accessToken
* @param {String} options.apiHost
* @param {String} options.storeHash
* @param {Number} options.channelId
*/
async getActiveTheme({ accessToken, apiHost, storeHash, channelId }) {
const activeTheme = await this._themeApiClient.getChannelActiveTheme({
accessToken,
apiHost,
storeHash,
channelId,
});

return activeTheme;
}

/**
* @param {Object} options
* @param {Boolean} options.saved
* @param {Object} options.activeTheme
* @param {String} options.accessToken
* @param {String} options.apiHost
* @param {String} options.storeHash
* @param {String} options.variationId
*/
async getThemeConfiguration({
saved,
activeTheme,
accessToken,
apiHost,
storeHash,
variationId,
}) {
const configurationId = saved
? activeTheme.saved_theme_configuration_uuid
: activeTheme.active_theme_configuration_uuid;

const remoteThemeConfiguration = await this._themeApiClient.getThemeConfiguration({
accessToken,
apiHost,
storeHash,
themeId: activeTheme.active_theme_uuid,
configurationId,
variationId,
});

return remoteThemeConfiguration;
}

/**
* @param {Object} options
* @param {String} options.variationId
* @param {String} options.activate
* @param {Object} options.remoteThemeConfiguration
* @param {Object} options.remoteThemeConfiguration
*/
async mergeThemeConfiguration({
variationId,
activate,
remoteThemeConfiguration,
saveConfigName,
}) {
const localConfig = await this._fsUtils.parseJsonFile('config.json');
let diffDetected = false;
let { settings } = localConfig;

if (variationId) {
({ settings } = localConfig.variations.find((v) => v.name === activate));
}

// For any keys the remote configuration has in common with the local configuration,
// overwrite the local configuration if the remote configuration differs
for (const [key, remoteVal] of Object.entries(remoteThemeConfiguration.settings)) {
if (!(key in settings)) {
continue;
}
const localVal = settings[key];

// Check for different types, and throw an error if they are found
if (typeof localVal !== typeof remoteVal) {
throw new Error(
`Theme configuration key "${key}" cannot be merged because it is not of the same type. ` +
`Remote configuration is of type ${typeof remoteVal} while local configuration is of type ${typeof localVal}.`,
);
}

// If a different value is found, overwrite the local config
if (!_.isEqual(localVal, remoteVal)) {
settings[key] = remoteVal;
diffDetected = true;
}
}

// Does a file need to be written?
if (diffDetected || saveConfigName !== 'config.json') {
if (diffDetected) {
console.log(
'ok'.green + ' -- Remote configuration merged with local configuration',
);
} else {
console.log(
'ok'.green +
' -- Remote and local configurations are in sync for all common keys',
);
}

await this._fs.promises.writeFile(saveConfigName, JSON.stringify(localConfig, null, 2));
console.log('ok'.green + ` -- Configuration written to ${saveConfigName}`);
} else {
console.log(
'ok'.green +
` -- Remote and local configurations are in sync for all common keys, no action taken`,
);
}
}
}

module.exports = stencilPull;
module.exports = StencilPull;
Loading