Skip to content

Commit 461292c

Browse files
authored
fix: Figure out asset folder name from jcr image path (#15)
1 parent 6140cf1 commit 461292c

File tree

5 files changed

+199
-161
lines changed

5 files changed

+199
-161
lines changed

README.md

+3-5
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ Add the npm script entries to your Edge Delivery project's `package.json`:
110110
1. Run the following command to log in to your AEM environment::
111111

112112
```
113-
npm run aem-login -- --aemurl https://author-env-url/
113+
npm run aem-login -- --aemurl https://author-env-url
114114
```
115115

116116
* You will be prompted to enter your username and password.
@@ -131,14 +131,12 @@ npm run aem-upload
131131

132132
You’ll be prompted to provide the following details:
133133
* Absolute Path to the Content Package: Path to the .zip file containing JCR pages, generated by the importer tool.
134-
* Absolute Path to the Image Mapping File: Path to `jcr-image-mappings.json`, which contains mappings for image urls and their corresponding JCR paths.
135-
* Site Name: The site name (must match the one provided during import).
134+
* Absolute Path to the Image Mapping File: Path to `image-mapping.json`, which contains mappings for image urls and their corresponding JCR paths.
136135

137136
```
138137
Checking for credentials...
139138
✔ Enter the absolute path to the content package: /Users/maji/Desktop/test/xwalkdemo.zip
140-
✔ Enter the absolute path to the jcr-image-mappings.json file: /Users/maji/Desktop/test/jcr-image-mappings.json
141-
✔ Enter the site name: xwalkdemo
139+
✔ Enter the absolute path to the image-mapping.json file: /Users/maji/Desktop/test/image-mapping.json
142140
```
143141

144142
Ensure that the content and images are successfully uploaded to your AEM instance. Verify the uploaded content through your AEM Author environment.

src/aem/downloadImages.js

+13-12
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import fetch from 'node-fetch';
1414
import fs from 'fs';
1515
import path from 'path';
16+
import chalk from 'chalk';
1617

1718
const CONTENT_DAM_PREFIX = '/content/dam';
1819

@@ -24,7 +25,7 @@ function ensureDirSync(directoryPath) {
2425
// Create the directory if it doesn't exist, including parent directories
2526
fs.mkdirSync(directoryPath, { recursive: true });
2627
} catch (err) {
27-
console.error('Error ensuring directory exists:', err);
28+
console.error(chalk.red('Error ensuring directory exists:', err));
2829
}
2930
}
3031

@@ -45,7 +46,7 @@ async function downloadImage(opts, imageUrl, jcrPath) {
4546

4647
if (!response.ok) {
4748
const msg = `Failed to fetch ${imageUrl}. Status: ${response.status}.`;
48-
console.error(msg);
49+
console.info(chalk.yellow(msg));
4950
throw new Error(msg);
5051
}
5152

@@ -62,10 +63,10 @@ async function downloadImage(opts, imageUrl, jcrPath) {
6263
});
6364
} catch (error) {
6465
if (attempt === maxRetries) {
65-
console.error(`Failed to download ${imageUrl} after ${maxRetries} attempts.`);
66+
console.error(chalk.red(`Failed to download ${imageUrl} after ${maxRetries} attempts.`));
6667
throw error;
6768
} else {
68-
console.info(`Retrying download (${attempt}/${maxRetries})...`);
69+
console.info(chalk.yellow(`Retrying download (${attempt}/${maxRetries})...`));
6970

7071
// Exponential backoff
7172
const delay = baseDelay * 2 ** (attempt - 1);
@@ -87,7 +88,7 @@ async function downloadImage(opts, imageUrl, jcrPath) {
8788
async function downloadImages(opts, imageUrlMap) {
8889
// Map over the entries and create a promise for each image download.
8990
const downloadPromises = Array.from(imageUrlMap.entries()).map(([imageUrl, jcrPath]) =>
90-
downloadImage(opts, imageUrl, jcrPath)
91+
downloadImage(opts, imageUrl, jcrPath),
9192
);
9293

9394
// Wait for all downloads to complete
@@ -97,12 +98,12 @@ async function downloadImages(opts, imageUrlMap) {
9798

9899
/**
99100
* Get a map of image URLs to JCR node paths from a JSON file.
100-
* @param {string} jcrImageMappingFile - The path to the JSON file containing image URLs and JCR node paths
101+
* @param {string} imageMappingFilePath - The path to the JSON file containing image URLs and JCR node paths
101102
* @returns {Map<string, string> | undefined} a map of image URLs to JCR node paths, or undefined if the file is invalid
102103
*/
103-
function getImageUrlMap(jcrImageMappingFile) {
104+
export function getImageUrlMap(imageMappingFilePath) {
104105
try {
105-
const data = fs.readFileSync(jcrImageMappingFile, 'utf8');
106+
const data = fs.readFileSync(imageMappingFilePath, 'utf8');
106107
const jsonData = JSON.parse(data);
107108

108109
if (typeof jsonData === 'object' && jsonData !== null) {
@@ -123,17 +124,17 @@ function getImageUrlMap(jcrImageMappingFile) {
123124
// Return undefined if there's an error reading the file or parsing JSON
124125
return undefined;
125126
}
126-
};
127+
}
127128

128129
/**
129130
* Function to download images present in given markdown file.
130131
*
131132
* @param opts - The options for downloading images
132-
* @param jcrImageMappingFile - The file containing mappings of image urls to their JCR node paths
133+
* @param imageMappingFilePath - The file containing mappings of image urls to their JCR node paths
133134
* @returns {Promise<void>}
134135
*/
135-
export default async function downloadImagesInMarkdown(opts, jcrImageMappingFile) {
136-
const imageUrlMap = getImageUrlMap(jcrImageMappingFile);
136+
export async function downloadImagesInMarkdown(opts, imageMappingFilePath) {
137+
const imageUrlMap = getImageUrlMap(imageMappingFilePath);
137138

138139
// Process the Map entries
139140
await downloadImages(opts, imageUrlMap);

src/aem/uploadImages.js

+35-8
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
import path from 'path';
1414
import fs from 'fs';
1515
import { FileSystemUploadOptions, FileSystemUpload } from '@adobe/aem-upload';
16-
import downloadImagesInMarkdown from './downloadImages.js';
16+
import { downloadImagesInMarkdown, getImageUrlMap } from './downloadImages.js';
17+
import chalk from 'chalk';
1718

1819
function cleanup(downloadLocation) {
1920
return fs.promises.rm(downloadLocation, { recursive: true, force: true });
@@ -69,7 +70,7 @@ function buildFileSystemUploadOptions(opts) {
6970
Authorization: `Basic ${getEncodedCredentials(username, password)}`,
7071
},
7172
})
72-
// If true and an asset with the given name already exists, the process will delete the existing
73+
// If 'true', and an asset with the given name already exists, the process will delete the existing
7374
// asset and create a new one with the same name and the new binary.
7475
.withUploadFileOptions({ replace: true });
7576
}
@@ -85,30 +86,56 @@ function createFileUploader() {
8586
// specific handling that should occur when a file finishes uploading successfully
8687
fileUpload.on('fileend', (data) => {
8788
const { fileName } = data;
88-
console.info(`Uploaded asset: ${fileName}`);
89+
console.info(chalk.yellow(`Uploaded asset: ${fileName}`));
8990
});
9091

9192
// specific handling that should occur when a file upload fails
9293
fileUpload.on('fileerror', (data) => {
9394
const { fileName, errors } = data;
94-
console.error(`Failed to upload asset: ${fileName}. ${errors.toString()}`);
95+
console.error(chalk.red(`Failed to upload asset: ${fileName}. ${errors.toString()}`));
9596
});
9697

9798
return fileUpload;
9899
}
99100

101+
/**
102+
* Get the AEM asset folder name from the image mapping file.
103+
*/
104+
function getAemAssetFolderName(imageMappingFilePath) {
105+
// Get the image URL map from the image mapping file
106+
const imageUrlMap = getImageUrlMap(imageMappingFilePath);
107+
108+
// Look for jcr content path in the image URL map
109+
// check all entries in case the value is not present in the first entry due to some reason
110+
for (const jcrAssetPath of imageUrlMap.values()) {
111+
if (jcrAssetPath) { // Check if the value is not empty
112+
const match = jcrAssetPath.match(/^\/content\/dam\/([^/]+)/);
113+
if (match) {
114+
return match[1];
115+
}
116+
}
117+
}
118+
119+
return null;
120+
}
121+
100122
/**
101123
* Upload images from urls in markdown file to AEM.
102124
*
103125
* @param opts - The options for uploading images to AEM
104-
* @param jcrImageMappingFile - The file containing mapping of image urls to their JCR node paths
105126
* @returns {Promise<UploadResult>} The result of the upload operation
106127
*/
107-
export default async function uploadImagesToAEM(opts, jcrImageMappingFile) {
108-
const downloadLocation = path.join(process.cwd(), opts.baseAssetFolderName);
128+
export default async function uploadImagesToAEM(opts) {
129+
const { imageMappingFilePath } = opts;
130+
const aemAssetFolderName = getAemAssetFolderName(imageMappingFilePath);
131+
if (!aemAssetFolderName) {
132+
throw new Error('No valid AEM asset path found in the JCR image mapping file.');
133+
}
134+
console.log(chalk.yellow(`Uploading images to AEM asset folder: ${aemAssetFolderName}`));
135+
const downloadLocation = path.join(process.cwd(), aemAssetFolderName);
109136

110137
// download images from the image mapping file
111-
await downloadImagesInMarkdown({ maxRetries: 3, downloadLocation }, jcrImageMappingFile);
138+
await downloadImagesInMarkdown({ maxRetries: 3, downloadLocation }, imageMappingFilePath);
112139

113140
// upload all assets in given folder
114141
const fileUpload = createFileUploader();

src/aem/uploadPackage.js

+9-8
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import fs from 'fs';
1313
import fetch from 'node-fetch';
1414
import FormData from 'form-data';
15+
import chalk from "chalk";
1516

1617
const BASE_DELAY = 5000; // base delay in milliseconds
1718

@@ -96,16 +97,16 @@ async function uploadPackageWithRetry(endpoint, packagePath, authHeader, maxRetr
9697
const formData = createFormData(packagePath);
9798
const uploadResponse = await uploadPackage(endpoint, authHeader, formData);
9899
const uploadResponseBody = await parseJsonResponse(uploadResponse);
99-
console.info(`Package uploaded successfully to ${uploadResponseBody.path}`);
100+
console.info(chalk.yellow(`Package uploaded successfully to ${uploadResponseBody.path}`));
100101

101102
return uploadResponseBody;
102103
} catch (error) {
103104
if (attempt === maxRetries) {
104-
console.error('Max retries reached. Failed to upload package.');
105+
console.error(chalk.red('Max retries reached. Failed to upload package.'));
105106
throw error;
106107
} else {
107108
const retryDelay = BASE_DELAY * 2 ** (attempt - 1);
108-
console.warn(`Retrying package upload (${attempt}/${maxRetries}) in ${retryDelay}ms...`);
109+
console.warn(chalk.yellow(`Retrying package upload (${attempt}/${maxRetries}) in ${retryDelay}ms...`));
109110
await new Promise((resolve) => {
110111
setTimeout(resolve, retryDelay);
111112
});
@@ -127,16 +128,16 @@ async function installPackageWithRetry(endpoint, authHeader, maxRetries = 3) {
127128
// Install package
128129
const installResponse = await installPackage(endpoint, authHeader);
129130
const installResponseBody = await parseJsonResponse(installResponse);
130-
console.info(`Package installed successfully at ${endpoint}.`);
131+
console.info(chalk.yellow(`Package installed successfully at ${endpoint}.`));
131132
return installResponseBody;
132133

133134
} catch (error) {
134135
if (attempt === maxRetries) {
135-
console.error('Max retries reached. Failed to install package.');
136+
console.error(chalk.red('Max retries reached. Failed to install package.'));
136137
throw error;
137138
} else {
138139
const retryDelay = BASE_DELAY * 2 ** (attempt - 1);
139-
console.warn(`Retrying package install (${attempt}/${maxRetries}) in ${retryDelay}ms...`);
140+
console.warn(chalk.yellow(`Retrying package install (${attempt}/${maxRetries}) in ${retryDelay}ms...`));
140141
await new Promise((resolve) => {
141142
setTimeout(resolve, retryDelay);
142143
});
@@ -151,9 +152,9 @@ async function installPackageWithRetry(endpoint, authHeader, maxRetries = 3) {
151152
* @param {string} packagePath - The path to the content package file path
152153
* @returns {Promise<unknown>} The response from the AEM server.
153154
*/
154-
export default async function uploadPackageToAEM(opts, packagePath) {
155+
export default async function uploadPackageToAEM(opts) {
155156
const {
156-
username, password, targetAEMUrl, maxRetries = 3,
157+
username, password, targetAEMUrl, maxRetries = 3, packagePath,
157158
} = opts;
158159

159160
if (!username || !password || !targetAEMUrl || !packagePath) {

0 commit comments

Comments
 (0)