Skip to main content
Glama
asset-operations.ts6.9 kB
/** * Asset Operations Module * Handles all AEM DAM (Digital Asset Management) operations including upload, update, delete, and metadata management */ import { AxiosInstance } from 'axios'; import { IAEMConnector, UploadAssetRequest, UpdateAssetRequest, DeleteAssetRequest, AssetResponse, AssetMetadataResponse, DeleteResponse, ILogger, AEMConfig } from '../interfaces/index.js'; import { AEMOperationError, createAEMError, handleAEMHttpError, safeExecute, createSuccessResponse, AEM_ERROR_CODES, isValidContentPath } from '../error-handler.js'; export class AssetOperations implements Partial<IAEMConnector> { constructor( private httpClient: AxiosInstance, private logger: ILogger, private config: AEMConfig ) {} /** * Upload a new asset to AEM DAM */ async uploadAsset(request: UploadAssetRequest): Promise<AssetResponse> { return safeExecute<AssetResponse>(async () => { const { parentPath, fileName, fileContent, mimeType, metadata = {} } = request; if (!isValidContentPath(parentPath)) { throw createAEMError( AEM_ERROR_CODES.INVALID_PARAMETERS, `Invalid parent path: ${String(parentPath)}`, { parentPath } ); } const assetPath = `${parentPath}/${fileName}`; try { // Use proper AEM DAM asset upload via Sling POST servlet const formData = new URLSearchParams(); // Set the file content (base64 or binary) if (typeof fileContent === 'string') { // Assume base64 encoded content formData.append('file', fileContent); } else { formData.append('file', String(fileContent)); } // Set required Sling POST parameters for asset creation formData.append('fileName', fileName); formData.append(':operation', 'import'); formData.append(':contentType', 'json'); formData.append(':replace', 'true'); formData.append('jcr:primaryType', 'dam:Asset'); if (mimeType) { formData.append('jcr:content/jcr:mimeType', mimeType); } // Add metadata to jcr:content/metadata node Object.entries(metadata).forEach(([key, value]) => { formData.append(`jcr:content/metadata/${key}`, String(value)); }); const response = await this.httpClient.post(assetPath, formData, { headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, }); // Verify the asset was created const verificationResponse = await this.httpClient.get(`${assetPath}.json`); return createSuccessResponse({ success: true, assetPath, fileName, mimeType, metadata, uploadResponse: response.data, assetData: verificationResponse.data, timestamp: new Date().toISOString(), }, 'uploadAsset') as AssetResponse; } catch (error: any) { // Fallback to alternative DAM API if available try { const damResponse = await this.httpClient.post('/api/assets' + parentPath, { fileName, fileContent, mimeType, metadata }); return createSuccessResponse({ success: true, assetPath, fileName, mimeType, metadata, uploadResponse: damResponse.data, fallbackUsed: 'DAM API', timestamp: new Date().toISOString(), }, 'uploadAsset') as AssetResponse; } catch (fallbackError: any) { throw handleAEMHttpError(error, 'uploadAsset'); } } }, 'uploadAsset'); } /** * Update an existing asset in AEM DAM */ async updateAsset(request: UpdateAssetRequest): Promise<AssetResponse> { return safeExecute<AssetResponse>(async () => { const { assetPath, metadata, fileContent, mimeType } = request; if (!isValidContentPath(assetPath)) { throw createAEMError( AEM_ERROR_CODES.INVALID_PARAMETERS, `Invalid asset path: ${String(assetPath)}`, { assetPath } ); } const formData = new URLSearchParams(); // Update file content if provided if (fileContent) { formData.append('file', fileContent); if (mimeType) { formData.append('jcr:content/jcr:mimeType', mimeType); } } // Update metadata if provided if (metadata && typeof metadata === 'object') { Object.entries(metadata).forEach(([key, value]) => { formData.append(`jcr:content/metadata/${key}`, String(value)); }); } try { const response = await this.httpClient.post(assetPath, formData, { headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, }); // Verify the update const verificationResponse = await this.httpClient.get(`${assetPath}.json`); return createSuccessResponse({ success: true, assetPath, fileName: assetPath.split('/').pop() || 'unknown', updatedMetadata: metadata, updateResponse: response.data, assetData: verificationResponse.data, timestamp: new Date().toISOString(), }, 'updateAsset') as AssetResponse; } catch (error: any) { throw handleAEMHttpError(error, 'updateAsset'); } }, 'updateAsset'); } /** * Delete an asset from AEM DAM */ async deleteAsset(request: DeleteAssetRequest): Promise<DeleteResponse> { return safeExecute<DeleteResponse>(async () => { const { assetPath, force = false } = request; if (!isValidContentPath(assetPath)) { throw createAEMError( AEM_ERROR_CODES.INVALID_PARAMETERS, `Invalid asset path: ${String(assetPath)}`, { assetPath } ); } await this.httpClient.delete(assetPath); return createSuccessResponse({ success: true, deletedPath: assetPath, force, timestamp: new Date().toISOString(), }, 'deleteAsset') as DeleteResponse; }, 'deleteAsset'); } /** * Get asset metadata from AEM DAM */ async getAssetMetadata(assetPath: string): Promise<AssetMetadataResponse> { return safeExecute<AssetMetadataResponse>(async () => { const response = await this.httpClient.get(`${assetPath}.json`); const metadata = response.data['jcr:content']?.metadata || {}; return createSuccessResponse({ assetPath, metadata, fullData: response.data, }, 'getAssetMetadata') as AssetMetadataResponse; }, 'getAssetMetadata'); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/indrasishbanerjee/aem-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server