generate_storage_sas_link
Generate temporary Azure Storage access URLs for Optimizely DXP containers. Create time-limited SAS links for read or write operations to share with external tools or scripts.
Instructions
🔗 Generate temporary Shared Access Signature URL for storage container. INSTANT: <1s. Returns time-limited URL for direct Azure Storage access (read or write). Default: 24 hour expiration, read-only. Set retentionHours for custom expiration (1-168 hours). Set writable=true for write access. Use for external tools, scripts, or direct blob access. Required: container, environment. Returns SAS URL string. URL expires after specified retention period.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| environment | Yes | ||
| containerName | Yes | ||
| projectName | No | ||
| projectId | No | ||
| permissions | No | Read | |
| expiryHours | No | ||
| apiKey | No | ||
| apiSecret | No |
Implementation Reference
- lib/tools/storage-tools.ts:321-367 (handler)The primary handler function for the 'generate_storage_sas_link' tool. It validates input, resolves project credentials if needed, defaults environment to Production, calls the core generateStorageSasLink method, and handles structured response formatting.static async handleGenerateStorageSasLink(args: StorageArgs): Promise<any> { try { // Check if this is a self-hosted project if (args.isSelfHosted || args.connectionString) { return ResponseBuilder.invalidParams('SAS link generation is not available for self-hosted projects. Self-hosted projects already have direct access via the connection string.'); } // Validate container name is provided if (!args.containerName) { return ResponseBuilder.invalidParams('Missing required parameter: containerName'); } // Resolve project configuration if credentials not provided let resolvedArgs = args; if (!args.apiKey || !args.apiSecret || !args.projectId) { const resolved = ProjectTools.resolveCredentials(args); if (!resolved.success || !resolved.credentials) { return ResponseBuilder.invalidParams('Missing required parameters. Either provide apiKey/apiSecret/projectId or configure a project.'); } resolvedArgs = { ...args, apiKey: resolved.credentials.apiKey || undefined, apiSecret: resolved.credentials.apiSecret || undefined, projectId: resolved.credentials.projectId || undefined, projectName: resolved.project?.name }; } // Default to Production if no environment specified if (!resolvedArgs.environment) { resolvedArgs.environment = 'Production'; } const result = await this.generateStorageSasLink(resolvedArgs); // DXP-66: Check if result is structured response with data and message if (result && typeof result === 'object' && 'data' in result && 'message' in result) { return ResponseBuilder.successWithStructuredData(result.data, result.message); } // Fallback for legacy string responses return ResponseBuilder.success(result); } catch (error: any) { // console.error('Generate SAS link error:', error); return ResponseBuilder.internalError('Failed to generate SAS link', error.message); } }
- lib/tools/storage-tools.ts:369-412 (helper)Core implementation logic that extracts parameters, determines permissions, calls DXPRestClient.getContainerSasLink to generate the SAS URL via Optimizely DXP REST API, and formats the result.static async generateStorageSasLink(args: StorageArgs): Promise<SasLinkResult | string> { // Extract parameters from args const apiKey = args.apiKey!; const apiSecret = args.apiSecret!; const projectId = args.projectId!; const { environment, containerName, permissions = 'Read', expiryHours = 24 } = args; // Convert expiryHours to validMinutes for display const validMinutes = expiryHours * 60; // console.error(`Generating SAS link for container ${containerName} in ${environment}`); // DXP-102: Use REST API instead of PowerShell (3-10x faster, no PowerShell dependency) try { // Determine if writable based on permissions const writable = (permissions === 'Write' || permissions === 'Delete'); // Convert hours to retention hours for API const retentionHours = Math.ceil(expiryHours); const result: SasLinkResponse = await DXPRestClient.getContainerSasLink( projectId, apiKey, apiSecret, environment!, containerName!, { retentionHours: retentionHours, writable: writable }, { apiUrl: args.apiUrl } // Support custom API URLs ); // Format response if (result) { return this.formatSasLink(result, containerName!, environment!, permissions, validMinutes); } return ResponseBuilder.addFooter('SAS link generation completed'); } catch (error: any) { // Handle REST API errors throw new Error(`Failed to generate SAS link for ${containerName}: ${error.message}`); } }
- lib/tools/storage-tools.ts:66-80 (schema)Type definition for input arguments (StorageArgs) used by generate_storage_sas_link, including required fields like containerName, environment, and optional credentials and expiry.interface StorageArgs { apiKey?: string; apiSecret?: string; projectId?: string; projectName?: string; environment?: string; containerName?: string; permissions?: string; expiryHours?: number; connectionString?: string; isSelfHosted?: boolean; apiUrl?: string; limit?: number; offset?: number; }
- lib/tools/storage-tools.ts:414-451 (helper)Helper function to format the SAS link response with human-readable message, tips, and structured data including expiry time.static formatSasLink(data: SasLinkResponse, containerName: string, environment: string, permissions: string, validMinutes: number): SasLinkResult { const { FORMATTING: { STATUS_ICONS } } = Config; let response = `${STATUS_ICONS.SUCCESS} **Storage SAS Link Generated**\n\n`; response += `**Container:** ${containerName}\n`; response += `**Environment:** ${environment}\n`; response += `**Permissions:** ${permissions}\n`; response += `**Valid for:** ${validMinutes} minutes\n\n`; if (data.sasLink || data.url) { response += `${STATUS_ICONS.UNLOCK} **SAS URL:**\n`; response += `\`\`\`\n${data.sasLink || data.url}\n\`\`\`\n\n`; response += `**Note:** This link expires after ${validMinutes} minutes.\n`; } const tips = [ 'Use this URL to access the storage container', 'The link includes authentication credentials', 'Do not share this link publicly' ]; response += '\n' + ResponseBuilder.formatTips(tips); // DXP-66: Return structured data and message const structuredData: SasLinkData = { containerName: containerName, environment: environment, permissions: permissions, validMinutes: validMinutes, expiresAt: new Date(Date.now() + validMinutes * 60000).toISOString(), sasUrl: data.sasLink || data.url || null }; return { data: structuredData, message: ResponseBuilder.addFooter(response) }; }