Skip to content
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
18 changes: 14 additions & 4 deletions nx/blocks/loc/utils/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,25 @@ export function getBasePath({ prefix, path }) {
return path.startsWith(prefix) ? path.replace(prefix, '') : path;
}

/**
* Create snapshot prefix path if snapshot is provided
* @param {string|undefined} snapshot - The snapshot name
* @returns {string} The snapshot prefix path
*/
export function createSnapshotPrefix(snapshot) {
return snapshot ? `/.snapshots/${snapshot}` : '';
}

/**
* Convert a path to DA and AEM formatted w/ optional destination language prefix
*
* @param config The path config.
* @param config.path An AEM-formatted (no org, site, index, .html) supplied path.
* @param config.sourcePrefix The prefix to remove.
* @param config.destPrefix The prefix to attach.
* @param config.snapshotPrefix The snapshot prefix to prepend to paths.
*/
export function convertPath({ path, sourcePrefix, destPrefix }) {
export function convertPath({ path, sourcePrefix, destPrefix, snapshotPrefix = '' }) {
const prefix = sourcePrefix === '/' || !sourcePrefix ? '' : sourcePrefix;

// Ensure the path doesn't already have the prefix
Expand All @@ -80,11 +90,11 @@ export function convertPath({ path, sourcePrefix, destPrefix }) {
// We also use ext to determine things like conflict behavior
const { path: daBasePath, ext } = getExtPath(aemBasePath);

const paths = { daBasePath, aemBasePath, ext };
const paths = { daBasePath: `${snapshotPrefix}${daBasePath}`, aemBasePath: `${snapshotPrefix}${aemBasePath}`, ext };

if (destPrefix) {
paths.daDestPath = `${destPrefix}${daBasePath}`;
paths.aemDestPath = `${destPrefix}${aemBasePath}`;
paths.daDestPath = `${snapshotPrefix}${destPrefix}${daBasePath}`;
paths.aemDestPath = `${snapshotPrefix}${destPrefix}${aemBasePath}`;
}

return paths;
Expand Down
9 changes: 5 additions & 4 deletions nx/blocks/loc/views/basics/basics.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,21 @@ class NxLocBasics extends LitElement {
}

setupProject() {
const { org, site, title, urls } = this.project;
const { org, site, title, snapshot, urls } = this.project;

this._title = title;

// If the existing project has URLs, format them down to plain text
this._textUrls = urls ? this.formatUrls(org, site, urls) : MOCK_URLS;
this._textUrls = urls ? this.formatUrls(org, site, urls, snapshot) : MOCK_URLS;
}

formatTitle({ target }) {
this._title = target.value.replaceAll(/[^a-zA-Z0-9]/g, '-').toLowerCase();
}

formatUrls(org, site, urls) {
return urls.map((url) => `https://main--${site}--${org}.aem.page${url.suppliedPath}`).join('\n');
formatUrls(org, site, urls, snapshot) {
const baseUrl = snapshot ? `${snapshot}--main--${site}--${org}.aem.reviews` : `main--${site}--${org}.aem.page`;
return urls.map((url) => `https://${baseUrl}${url.suppliedPath}`).join('\n');
}

async getUpdates(view) {
Expand Down
104 changes: 93 additions & 11 deletions nx/blocks/loc/views/basics/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,85 @@ function getMessage(text) {
return { text, type: 'error' };
}

/**
* Parse hostname into parts separated by '--'
* @param {string} hostname - The hostname to parse
* @returns {string[]} Array of hostname parts
*/
function parseHostnameParts(hostname) {
return hostname.split('.')[0].split('--');
}

/**
* Transform a snapshot URL to the proper reviews format
* @param {string} url - The URL to transform
* @param {string} site - The site name
* @param {string} org - The organization name
* @returns {URL} The transformed URL
*/
function transformSnapshotUrl(url, site, org) {
const properUrl = new URL(url);
if (properUrl?.pathname.startsWith('/.snapshots/')) {
const pathFragments = properUrl.pathname.split('/');
if (pathFragments.length > 2) {
const snapshotName = pathFragments[2];
const newHostName = `${snapshotName}--main--${site}--${org}.aem.reviews`;
return new URL(`${properUrl.protocol}//${newHostName}/${pathFragments.slice(3).join('/')}`);
}
}
return properUrl;
}

/**
* Extract site and organization from hostname
* @param {string} hostname - The hostname to parse
* @returns {Object} Object containing site and org
*/
function extractSiteAndOrg(hostname) {
const parts = parseHostnameParts(hostname);
const [site, org] = parts.slice(-2);
return { site, org };
}

/**
* Check if URL is a snapshot URL and extract snapshot name
* @param {string} hostname - The hostname to check
* @returns {string|undefined} The snapshot name or undefined
*/
function extractSnapshotName(hostname) {
const parts = parseHostnameParts(hostname);
return parts.length === 4 && hostname.includes('.reviews') ? parts[0] : undefined;
}

/**
* Extract all relevant information from a URL
* @param {string} url - The URL to analyze
* @returns {Object} Object containing hostname, site, org, and snapshot
*/
function extractUrlInformation(url) {
try {
const { hostname } = new URL(url);
const { site, org } = extractSiteAndOrg(hostname);
const finalUrl = transformSnapshotUrl(url, site, org);
const snapshot = extractSnapshotName(finalUrl.hostname);

return {
hostname: finalUrl.hostname,
site,
org,
snapshot,
};
} catch (e) {
return {};
}
}

/**
* Format and validate basic project information from title and URL paths
* @param {string} title - The project title
* @param {string} paths - Newline-separated list of AEM URLs
* @returns {Object} Object containing either updates or error message
*/
export default function formatBasics(title, paths) {
if (!title) {
return { message: getMessage('Please enter a title') };
Expand All @@ -17,10 +96,22 @@ export default function formatBasics(title, paths) {
// Remove empties
urls = urls.filter((url) => url);

// Get first hostname
const {
hostname,
site,
org,
snapshot,
} = extractUrlInformation(urls[0]);

if (!(site || org)) {
return { message: getMessage('Please use AEM URLs') };
}

// Convert to proper URLs
urls = urls.map((url) => {
try {
return new URL(url);
return transformSnapshotUrl(url, site, org);
} catch (e) {
return { error: true };
}
Expand All @@ -30,22 +121,13 @@ export default function formatBasics(title, paths) {
return { message: getMessage('Please use AEM URLs.') };
}

// Get first hostname
const { hostname } = urls[0];

// Ensure all URLs have same hostname
const filtered = urls.filter((url) => url.hostname === hostname);
if (filtered.length !== urls.length) return { message: getMessage('URLs are not from the same site.') };

// Subdomain split
const [site, org] = hostname.split('.')[0].split('--').slice(1).slice(-2);
if (!(site || org)) {
return { message: getMessage('Please use AEM URLs') };
}

// Flatten down to pure pathnames
const hrefs = urls.map((url) => ({ suppliedPath: url.pathname }));

// Return the updates we want to persist
return { updates: { org, site, title, urls: hrefs } };
return { updates: { org, site, snapshot, title, urls: hrefs } };
}
22 changes: 16 additions & 6 deletions nx/blocks/loc/views/rollout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { DA_ORIGIN } from '../../../../public/utils/constants.js';
import { Queue } from '../../../../public/utils/tree.js';
import { daFetch } from '../../../../utils/daFetch.js';
import { mergeCopy, overwriteCopy } from '../../project/index.js';
import { convertPath } from '../../utils/utils.js';
import { convertPath, createSnapshotPrefix } from '../../utils/utils.js';

function getTitle(status) {
const title = {
Expand Down Expand Up @@ -102,26 +102,35 @@ async function rolloutLangLocales(title, lang, urls, behavior) {
return results;
}

function formatLangUrls(org, site, sourceLocation, lang, urls) {
function formatLangUrls(org, site, sourceLocation, lang, urls, snapshot) {
const snapshotPrefix = createSnapshotPrefix(snapshot);
return urls.map((url) => {
const convertConf = {
path: url.suppliedPath,
sourcePrefix: sourceLocation,
destPrefix: lang.location,
snapshotPrefix,
};
const { daDestPath, aemBasePath, ext } = convertPath(convertConf);
const source = `/${org}/${site}${daDestPath}`;
return { source, aemBasePath, ext };
});
}

function formatRolloutUrls(org, site, lang, urls) {
function formatRolloutUrls(org, site, lang, urls, snapshot) {
const snapshotPrefix = createSnapshotPrefix(snapshot);
return lang.locales.reduce((acc, locale) => {
const localeUrls = urls.map((langUrl) => {
const { daDestPath } = convertPath({ path: langUrl.aemBasePath, destPrefix: locale.code });
const { daDestPath } = convertPath({
path: langUrl.aemBasePath,
sourcePrefix: snapshotPrefix,
destPrefix: locale.code,
snapshotPrefix,
});
return {
hasExt: langUrl.ext === 'json',
sourceContent: langUrl.content,
source: `/${org}/${site}${langUrl.aemBasePath}`,
destination: `/${org}/${site}${daDestPath}`,
};
});
Expand All @@ -133,6 +142,7 @@ function formatRolloutUrls(org, site, lang, urls) {
export async function rolloutLang({
org,
site,
snapshot,
title,
options,
lang,
Expand All @@ -146,12 +156,12 @@ export async function rolloutLang({
const behavior = options['rollout.conflict.behavior'];

// Determine all sources are valid before continuing
const langUrls = formatLangUrls(org, site, sourceLocation, lang, projectUrls);
const langUrls = formatLangUrls(org, site, sourceLocation, lang, projectUrls, snapshot);
let { errors, message, urls } = await fetchLangSources(lang, langUrls);
if (errors) return { errors, message };

// Convert base lang urls to the full locale list
const urlsToSave = formatRolloutUrls(org, site, lang, urls);
const urlsToSave = formatRolloutUrls(org, site, lang, urls, snapshot);

// Perform the actual rollout
({ errors, message, urls } = await rolloutLangLocales(title, lang, urlsToSave, behavior));
Expand Down
13 changes: 7 additions & 6 deletions nx/blocks/loc/views/sync/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { convertPath } from '../../utils/utils.js';
import { convertPath, createSnapshotPrefix } from '../../utils/utils.js';

function getFullPath(path, sourcePrefix, destPrefix) {
return convertPath({ path, sourcePrefix, destPrefix });
}

export function getSyncUrls(org, site, location, urls) {
export function getSyncUrls(org, site, location, urls, snapshot) {
const snapshotPrefix = createSnapshotPrefix(snapshot);
return urls.map((url) => {
const {
daBasePath,
Expand All @@ -16,10 +17,10 @@ export function getSyncUrls(org, site, location, urls) {

const opts = {
...url,
sourceView: aemBasePath,
destView: aemDestPath,
source: `/${org}/${site}${daBasePath}`,
destination: `/${org}/${site}${daDestPath}`,
sourceView: `${snapshotPrefix}${aemBasePath}`,
destView: `${snapshotPrefix}${aemDestPath}`,
source: `/${org}/${site}${snapshotPrefix}${daBasePath}`,
destination: `/${org}/${site}${snapshotPrefix}${daDestPath}`,
hasExt: ext === 'json',
};

Expand Down
4 changes: 2 additions & 2 deletions nx/blocks/loc/views/sync/sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ class NxLocSync extends LitElement {
}

getSyncUrls() {
const { org, site, options, urls } = this.project;
const { org, site, options, urls, snapshot } = this.project;
const sendLocation = options['source.language']?.location || '/';
this._syncUrls = getSyncUrls(org, site, sendLocation, urls);
this._syncUrls = getSyncUrls(org, site, sendLocation, urls, snapshot);
}

getPersistedUrls() {
Expand Down
11 changes: 8 additions & 3 deletions nx/blocks/loc/views/translate/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DA_ORIGIN } from '../../../../public/utils/constants.js';
import { Queue } from '../../../../public/utils/tree.js';
import { daFetch } from '../../../../utils/daFetch.js';
import { convertPath, fetchConfig, formatPath } from '../../utils/utils.js';
import { convertPath, createSnapshotPrefix, fetchConfig, formatPath } from '../../utils/utils.js';
import { mergeCopy, overwriteCopy } from '../../project/index.js';

let CONNECTOR;
Expand All @@ -12,15 +12,17 @@ export async function setupConnector(service) {
return CONNECTOR;
}

export async function getUrls(org, site, service, sourceLocation, urls, fetchContent) {
export async function getUrls(org, site, service, sourceLocation, urls, fetchContent, snapshot) {
const { connector } = service;
const snapshotPrefix = createSnapshotPrefix(snapshot);

// Format the URLs to get all possible path variations
const formattedUrls = urls.map((url) => {
const converConf = {
path: url.suppliedPath,
sourcePrefix: sourceLocation,
destPrefix: sourceLocation,
snapshotPrefix,
};
const formatted = convertPath(converConf);

Expand Down Expand Up @@ -75,6 +77,7 @@ export async function getUrls(org, site, service, sourceLocation, urls, fetchCon
async function saveLang({
org,
site,
snapshot,
title,
service,
connector,
Expand All @@ -83,8 +86,10 @@ async function saveLang({
urls,
sendMessage,
}) {
const snapshotPrefix = createSnapshotPrefix(snapshot);

const urlsToSave = urls.map((url) => {
const { daDestPath } = convertPath({ path: url.basePath, sourcePrefix: '/', destPrefix: lang.location });
const { daDestPath } = convertPath({ path: url.basePath, sourcePrefix: '/', destPrefix: lang.location, snapshotPrefix });
return { ...url, destination: `/${org}/${site}${daDestPath}` };
});

Expand Down
7 changes: 4 additions & 3 deletions nx/blocks/loc/views/translate/translate.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ class NxLocTranslate extends LitElement {
}

async fetchUrls(service, fetchContent) {
const { org, site } = this.project;
const { org, site, snapshot } = this.project;
const sourceLocation = this._options['source.language']?.location || '/';

return getUrls(org, site, service, sourceLocation, this._urls, fetchContent);
return getUrls(org, site, service, sourceLocation, this._urls, fetchContent, snapshot);
}

async getBaseTranslationConf(fetchContent) {
Expand All @@ -101,14 +101,15 @@ class NxLocTranslate extends LitElement {
sendMessage: this.handleMessage.bind(this),
};

const { org, site, title, options } = this.project;
const { org, site, title, options, snapshot } = this.project;
const { _service: service, _translateLangs: langs } = this;

const { urls } = await this.fetchUrls(service, fetchContent);

return {
org,
site,
snapshot,
title,
service,
options,
Expand Down
Loading