@capillarytech/creatives-library 8.0.153 → 8.0.154

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22,6 +22,7 @@ describe('Test whatsapp actions', () => {
22
22
  accountId: '2000222347',
23
23
  accessToken: 'DTpZEh4VK',
24
24
  hostName: 'gupshupwhatsappbulk',
25
+ baseUrl: 'https://api.gupshup.io/whatsapp/v1',
25
26
  },
26
27
  },
27
28
  },
@@ -2,7 +2,6 @@ import {
2
2
  HOST_HAPTIC,
3
3
  HAPTIC_CATEGORY_OPTIONS,
4
4
  CATEGORY_OPTIONS_MAP,
5
- mediaTypeOptions,
6
5
  } from '../constants';
7
6
 
8
7
  // Mock API calls
@@ -84,6 +84,21 @@ describe('Creatives Whatsapp test1/>', () => {
84
84
  loadingTags={args.loadingTags}
85
85
  metaEntities={[]}
86
86
  getDefaultTags={true}
87
+ Templates={{
88
+ senderDetails: {
89
+ status: 'SUCCESS',
90
+ domainProperties: [
91
+ {
92
+ domainProperties: {
93
+ connectionProperties: {
94
+ sourceAccountIdentifier: '2000222347',
95
+ baseUrl: 'https://api.gupshup.io/whatsapp/v1',
96
+ },
97
+ },
98
+ },
99
+ ],
100
+ },
101
+ }}
87
102
  />
88
103
  </Provider>,
89
104
  );
@@ -240,7 +255,7 @@ describe('Creatives Whatsapp test1/>', () => {
240
255
 
241
256
  it('test addVariable in create mode', () => {
242
257
 
243
-
258
+
244
259
  renderHelper({});
245
260
  renderedComponent.update();
246
261
  renderedComponent.find('[data-testid="suffix-button"]').at(1).props().onClick();
@@ -365,6 +380,21 @@ describe('Creatives Whatsapp test2/>', () => {
365
380
  loadingTags={args.loadingTags}
366
381
  metaEntities={[]}
367
382
  getDefaultTags={true}
383
+ Templates={{
384
+ senderDetails: {
385
+ status: 'SUCCESS',
386
+ domainProperties: [
387
+ {
388
+ domainProperties: {
389
+ connectionProperties: {
390
+ sourceAccountIdentifier: '2000222347',
391
+ baseUrl: 'https://api.gupshup.io/whatsapp/v1',
392
+ },
393
+ },
394
+ },
395
+ ],
396
+ },
397
+ }}
368
398
  />
369
399
  </Provider>,
370
400
  );
@@ -530,7 +560,7 @@ describe('mediaTypeOptions', () => {
530
560
  templateCategory: WHATSAPP_CATEGORIES.marketing,
531
561
  });
532
562
  const hasCarousel = options.some(
533
- (opt) => Array.isArray(opt)
563
+ (opt) => Array.isArray(opt)
534
564
  ? opt.some((o) => o.key === 'CAROUSEL')
535
565
  : opt.key === 'CAROUSEL'
536
566
  ) || options.flat().some((o) => o.key === 'CAROUSEL');
@@ -543,7 +573,7 @@ describe('mediaTypeOptions', () => {
543
573
  templateCategory: WHATSAPP_CATEGORIES.marketing,
544
574
  });
545
575
  const hasCarousel = options.some(
546
- (opt) => Array.isArray(opt)
576
+ (opt) => Array.isArray(opt)
547
577
  ? opt.some((o) => o.key === 'CAROUSEL')
548
578
  : opt.key === 'CAROUSEL'
549
579
  ) || options.flat().some((o) => o.key === 'CAROUSEL');
@@ -556,7 +586,7 @@ describe('mediaTypeOptions', () => {
556
586
  templateCategory: WHATSAPP_CATEGORIES.utility,
557
587
  });
558
588
  const hasCarousel = options.some(
559
- (opt) => Array.isArray(opt)
589
+ (opt) => Array.isArray(opt)
560
590
  ? opt.some((o) => o.key === 'CAROUSEL')
561
591
  : opt.key === 'CAROUSEL'
562
592
  ) || options.flat().some((o) => o.key === 'CAROUSEL');
@@ -569,7 +599,7 @@ describe('mediaTypeOptions', () => {
569
599
  templateCategory: WHATSAPP_CATEGORIES.utility,
570
600
  });
571
601
  const hasCarousel = options.some(
572
- (opt) => Array.isArray(opt)
602
+ (opt) => Array.isArray(opt)
573
603
  ? opt.some((o) => o.key === 'CAROUSEL')
574
604
  : opt.key === 'CAROUSEL'
575
605
  ) || options.flat().some((o) => o.key === 'CAROUSEL');
@@ -85,7 +85,8 @@ export default {
85
85
  "authToken": "Bearer abcd",
86
86
  "sourceAccountIdentifier": "12345",
87
87
  "wabaId": "12345",
88
- "whatsapp_pool_id": "CAP001"
88
+ "whatsapp_pool_id": "CAP001",
89
+ "baseUrl": "https://api.whatsapp.com/v1"
89
90
  },
90
91
  "hostName": "karixwhatsappbulk"
91
92
  },
@@ -128,7 +129,8 @@ export default {
128
129
  "connectionProperties": {
129
130
  "password": "qrtCAJCz",
130
131
  "sourceAccountIdentifier": "123456",
131
- "userid": "123456"
132
+ "userid": "123456",
133
+ "baseUrl": "https://api.gupshup.io/whatsapp/v1"
132
134
  },
133
135
  "hostName": "gupshupwhatsappbulk"
134
136
  },
@@ -2587,7 +2589,7 @@ export default {
2587
2589
  loyaltyMetaData: {
2588
2590
  actionName: "SEND_COMMUNICATION_ACTION",
2589
2591
  },
2590
- getWhatsappAutoFillData:
2592
+ getWhatsappAutoFillData:
2591
2593
  < CapLabel
2592
2594
  className="whatsapp-autofill-btn"
2593
2595
  type="label21"
Binary file
@@ -1,290 +0,0 @@
1
-
2
- import {
3
- transformTemplateConfigWithMediaDetails,
4
- convertMediaTagsToUrls,
5
- convertUrlsToMediaTags,
6
- TRANSFORM_DIRECTION,
7
- MEDIA_CONTENT_TYPE,
8
- } from '../transformTemplateConfig';
9
- import { getMediaDetails } from '../../services/api';
10
- import { WHATSAPP } from '../../v2Containers/Whatsapp/constants';
11
-
12
- jest.mock('../../services/api', () => ({
13
- getMediaDetails: jest.fn(),
14
- }));
15
-
16
- const mockUrlToIdMapping = {
17
- 'http://example.com/image1.png': 'id1',
18
- 'http://example.com/video.mp4': 'id2',
19
- };
20
-
21
- const mockIdToUrlMapping = {
22
- id1: 'http://example.com/image1.png',
23
- id2: 'http://example.com/video.mp4',
24
- };
25
-
26
- describe('transformTemplateConfig', () => {
27
- beforeEach(() => {
28
- getMediaDetails.mockClear();
29
- // eslint-disable-next-line no-console
30
- console.warn = jest.fn();
31
- // eslint-disable-next-line no-console
32
- console.error = jest.fn();
33
- // eslint-disable-next-line no-console
34
- console.log = jest.fn();
35
- });
36
-
37
- describe('transformTemplateConfigWithMediaDetails', () => {
38
- it('should return original config for non-whatsapp channel', async () => {
39
- const templateConfig = { cards: [{ media: { url: 'some_url' } }] };
40
- const result = await transformTemplateConfigWithMediaDetails(
41
- templateConfig,
42
- 'SMS',
43
- 'media123',
44
- );
45
- expect(result).toEqual(templateConfig);
46
- expect(getMediaDetails).not.toHaveBeenCalled();
47
- });
48
-
49
- it('should return original config if no cards are present', async () => {
50
- const templateConfig = { cards: [] };
51
- const result = await transformTemplateConfigWithMediaDetails(
52
- templateConfig,
53
- WHATSAPP,
54
- 'media123',
55
- );
56
- expect(result).toEqual(templateConfig);
57
- });
58
-
59
- it('should return original config and warn if mediaDetailsId is missing', async () => {
60
- const templateConfig = { cards: [{ media: { url: 'some_url' } }] };
61
- const result = await transformTemplateConfigWithMediaDetails(
62
- templateConfig,
63
- WHATSAPP,
64
- null,
65
- );
66
- expect(result).toEqual(templateConfig);
67
- // eslint-disable-next-line no-console
68
- expect(console.warn).toHaveBeenCalledWith(
69
- 'mediaDetailsId is required for transformTemplateConfigWithMediaDetails',
70
- );
71
- });
72
-
73
- it('should handle forward transformation (URL to Tag)', async () => {
74
- getMediaDetails.mockResolvedValue({ response: mockUrlToIdMapping });
75
- const templateConfig = {
76
- cards: [
77
- { media: { url: 'http://example.com/image1.png' } },
78
- { media: { url: 'http://example.com/video.mp4' } },
79
- ],
80
- };
81
-
82
- const result = await transformTemplateConfigWithMediaDetails(
83
- templateConfig,
84
- WHATSAPP,
85
- 'media123',
86
- );
87
-
88
- expect(result.cards[0].media.url).toBe('{{media_content(id1)}}');
89
- expect(result.cards[1].media.url).toBe('{{media_content(id2)}}');
90
- });
91
-
92
- it('should handle reverse transformation (Tag to URL)', async () => {
93
- getMediaDetails.mockResolvedValue({ response: mockUrlToIdMapping });
94
- const templateConfig = {
95
- cards: [
96
- { media: { url: '{{media_content(id1)}}' } },
97
- { media: { url: '{{media_content(id2)}}' } },
98
- ],
99
- };
100
-
101
- const result = await transformTemplateConfigWithMediaDetails(
102
- templateConfig,
103
- WHATSAPP,
104
- 'media123',
105
- );
106
-
107
- expect(result.cards[0].media.url).toBe(mockIdToUrlMapping.id1);
108
- expect(result.cards[1].media.url).toBe(mockIdToUrlMapping.id2);
109
- });
110
-
111
- it('should handle smart transformation for mixed content', async () => {
112
- getMediaDetails.mockResolvedValue({ response: mockUrlToIdMapping });
113
- const templateConfig = {
114
- cards: [
115
- { media: { url: 'http://example.com/image1.png' } }, // URL
116
- { media: { url: '{{media_content(id2)}}' } }, // Tag
117
- ],
118
- };
119
-
120
- const result = await transformTemplateConfigWithMediaDetails(
121
- templateConfig,
122
- WHATSAPP,
123
- 'media123',
124
- );
125
-
126
- expect(result.cards[0].media.url).toBe('{{media_content(id1)}}');
127
- expect(result.cards[1].media.url).toBe(mockIdToUrlMapping.id2);
128
- });
129
-
130
- it('should force forward transformation', async () => {
131
- getMediaDetails.mockResolvedValue({ response: mockUrlToIdMapping });
132
- const templateConfig = {
133
- cards: [{ media: { url: 'http://example.com/image1.png' } }],
134
- };
135
-
136
- const result = await transformTemplateConfigWithMediaDetails(
137
- templateConfig,
138
- WHATSAPP,
139
- 'media123',
140
- { forceDirection: TRANSFORM_DIRECTION.FORWARD },
141
- );
142
-
143
- expect(result.cards[0].media.url).toBe('{{media_content(id1)}}');
144
- });
145
-
146
- it('should force reverse transformation', async () => {
147
- getMediaDetails.mockResolvedValue({ response: mockUrlToIdMapping });
148
- const templateConfig = {
149
- cards: [{ media: { url: '{{media_content(id1)}}' } }],
150
- };
151
-
152
- const result = await transformTemplateConfigWithMediaDetails(
153
- templateConfig,
154
- WHATSAPP,
155
- 'media123',
156
- { forceDirection: TRANSFORM_DIRECTION.REVERSE },
157
- );
158
-
159
- expect(result.cards[0].media.url).toBe(mockIdToUrlMapping.id1);
160
- });
161
-
162
- it('should handle API failure gracefully', async () => {
163
- const error = new Error('API Error');
164
- getMediaDetails.mockRejectedValue(error);
165
- const templateConfig = {
166
- cards: [{ media: { url: 'http://example.com/image1.png' } }],
167
- };
168
-
169
- const result = await transformTemplateConfigWithMediaDetails(
170
- templateConfig,
171
- WHATSAPP,
172
- 'media123',
173
- );
174
-
175
- // Fallback logic does not transform
176
- expect(result.cards[0].media.url).toBe('http://example.com/image1.png');
177
- // eslint-disable-next-line no-console
178
- expect(console.error).toHaveBeenCalledWith(
179
- 'Failed to fetch media details for transformation:',
180
- error,
181
- );
182
- });
183
-
184
- it('should warn when no media mapping is found for a URL', async () => {
185
- getMediaDetails.mockResolvedValue({ response: mockUrlToIdMapping });
186
- const templateConfig = {
187
- cards: [{ media: { url: 'http://example.com/unmapped.jpg' } }],
188
- };
189
-
190
- await transformTemplateConfigWithMediaDetails(
191
- templateConfig,
192
- WHATSAPP,
193
- 'media123',
194
- );
195
-
196
- // eslint-disable-next-line no-console
197
- expect(console.warn).toHaveBeenCalledWith(
198
- 'No media detail ID found for URL: http://example.com/unmapped.jpg',
199
- );
200
- });
201
-
202
- it('should warn when no URL is found for a media ID', async () => {
203
- getMediaDetails.mockResolvedValue({ response: mockUrlToIdMapping });
204
- const templateConfig = {
205
- cards: [{ media: { url: '{{media_content(unmapped_id)}}' } }],
206
- };
207
-
208
- await transformTemplateConfigWithMediaDetails(
209
- templateConfig,
210
- WHATSAPP,
211
- 'media123',
212
- );
213
-
214
- // eslint-disable-next-line no-console
215
- expect(console.warn).toHaveBeenCalledWith(
216
- 'No URL found for media ID: unmapped_id',
217
- );
218
- });
219
-
220
- it('should handle cards with non-string media url gracefully', async () => {
221
- getMediaDetails.mockResolvedValue({ response: mockUrlToIdMapping });
222
- const templateConfig = {
223
- cards: [
224
- { media: { url: 123 } },
225
- ],
226
- };
227
- const result = await transformTemplateConfigWithMediaDetails(templateConfig, WHATSAPP, 'media123');
228
- expect(result.cards[0].media.url).toBe(123);
229
- expect(console.warn).toHaveBeenCalledWith('No media detail ID found for URL: 123');
230
- });
231
-
232
- it('should use fallback to extract media id when API fails for forced forward transform', async () => {
233
- getMediaDetails.mockRejectedValue(new Error('API Error'));
234
- const templateConfig = {
235
- cards: [{ media: { url: '{{media_content(fallback_id)}}' } }],
236
- };
237
- const result = await transformTemplateConfigWithMediaDetails(
238
- templateConfig,
239
- WHATSAPP,
240
- 'media123',
241
- { forceDirection: TRANSFORM_DIRECTION.FORWARD },
242
- );
243
- expect(result.cards[0].media.url).toBe('{{media_content(fallback_id)}}');
244
- expect(console.error).toHaveBeenCalledWith(
245
- 'Failed to fetch media details for transformation:',
246
- expect.any(Error),
247
- );
248
- });
249
-
250
- it('should return original config if media details mapping is empty', async () => {
251
- getMediaDetails.mockResolvedValue({ response: {} });
252
- const templateConfig = {
253
- cards: [{ media: { url: 'http://example.com/image1.png' } }],
254
- };
255
- const result = await transformTemplateConfigWithMediaDetails(
256
- templateConfig,
257
- WHATSAPP,
258
- 'media123',
259
- );
260
- expect(result).toEqual(templateConfig);
261
- expect(console.warn).toHaveBeenCalledWith('No media details mapping found in API response');
262
- });
263
- });
264
-
265
- describe('convenience wrappers', () => {
266
- it('convertUrlsToMediaTags should force forward direction', async () => {
267
- getMediaDetails.mockResolvedValue({ response: mockUrlToIdMapping });
268
- const templateConfig = {
269
- cards: [{ media: { url: 'http://example.com/image1.png' } }],
270
- };
271
-
272
- const result = await convertUrlsToMediaTags(templateConfig, WHATSAPP, 'media123');
273
-
274
- expect(getMediaDetails).toHaveBeenCalledWith({ id: 'media123' });
275
- expect(result.cards[0].media.url).toBe('{{media_content(id1)}}');
276
- });
277
-
278
- it('convertMediaTagsToUrls should force reverse direction', async () => {
279
- getMediaDetails.mockResolvedValue({ response: mockUrlToIdMapping });
280
- const templateConfig = {
281
- cards: [{ media: { url: '{{media_content(id1)}}' } }],
282
- };
283
-
284
- const result = await convertMediaTagsToUrls(templateConfig, WHATSAPP, 'media123');
285
-
286
- expect(getMediaDetails).toHaveBeenCalledWith({ id: 'media123' });
287
- expect(result.cards[0].media.url).toBe(mockIdToUrlMapping.id1);
288
- });
289
- });
290
- });
@@ -1,253 +0,0 @@
1
- import cloneDeep from 'lodash/cloneDeep';
2
- import { getMediaDetails } from '../services/api';
3
- import { WHATSAPP } from '../v2Containers/Whatsapp/constants';
4
-
5
- // Enum-like constants to avoid magic strings
6
- export const MEDIA_CONTENT_TYPE = {
7
- MIXED: 'mixed',
8
- TAGS: 'tags',
9
- URLS: 'urls',
10
- NONE: 'none',
11
- };
12
-
13
- export const TRANSFORM_DIRECTION = {
14
- FORWARD: 'forward',
15
- REVERSE: 'reverse',
16
- SMART: 'smart',
17
- };
18
-
19
- /**
20
- * Helper function to extract media ID from URL patterns
21
- * @param {string} url - The media URL to extract ID from
22
- * @returns {string|null} - Extracted media ID or null if not found
23
- */
24
- function extractMediaIdFromUrl(url) {
25
- if (!url || typeof url !== 'string') {
26
- return null;
27
- }
28
-
29
-
30
- // Already contains media_content pattern - extract the ID
31
- const mediaContentMatch = url.match(/\{\{media_content\(([^)]+)\)\}\}/);
32
- if (mediaContentMatch) {
33
- return mediaContentMatch[1];
34
- }
35
-
36
- return null;
37
- }
38
-
39
- /**
40
- * Helper function to detect if template config contains media_content tags or URLs
41
- * @param {object} templateConfig The template configuration to analyze
42
- * @returns {string} MEDIA_CONTENT_TYPE.TAGS if contains media_content tags, MEDIA_CONTENT_TYPE.URLS if contains URLs, MEDIA_CONTENT_TYPE.MIXED if both, MEDIA_CONTENT_TYPE.NONE if neither
43
- */
44
- function detectMediaContentType(templateConfig) {
45
- if (!templateConfig?.cards?.length) {
46
- return MEDIA_CONTENT_TYPE.NONE;
47
- }
48
-
49
- let hasUrls = false;
50
- let hasTags = false;
51
-
52
- templateConfig.cards.forEach(card => {
53
- if (card?.media?.url) {
54
- const url = card.media.url;
55
- if (extractMediaIdFromTag(url)) {
56
- hasTags = true;
57
- } else if (extractMediaIdFromUrl(url)) {
58
- hasUrls = true;
59
- } else {
60
- // Regular URL that doesn't contain extractable media ID
61
- hasUrls = true;
62
- }
63
- }
64
- });
65
-
66
- if (hasUrls && hasTags) return MEDIA_CONTENT_TYPE.MIXED;
67
- if (hasTags) return MEDIA_CONTENT_TYPE.TAGS;
68
- if (hasUrls) return MEDIA_CONTENT_TYPE.URLS;
69
- return MEDIA_CONTENT_TYPE.NONE;
70
- }
71
-
72
- /**
73
- * Smart bidirectional transformation function that automatically detects and applies the correct transformation.
74
- * This is the recommended function to use when you have a media details ID.
75
- *
76
- * AUTOMATIC DETECTION:
77
- * - If template contains URLs → converts to {{media_content(id)}} tags
78
- * - If template contains {{media_content(id)}} tags → converts to URLs
79
- * - If template contains mixed content → applies appropriate transformation per card
80
- *
81
- * @param {object} templateConfig The template configuration to transform.
82
- * @param {string} channel The channel for which to transform the config.
83
- * @param {string} mediaDetailsId The ID to use when calling getMediaDetails API.
84
- * @param {object} options Additional options for the transformation.
85
- * @param {string} options.forceDirection Force transformation direction: 'forward' (URLs→tags) or 'reverse' (tags→URLs).
86
- * @returns {Promise<object>} Promise that resolves to the transformed template configuration.
87
- */
88
- export async function transformTemplateConfigWithMediaDetails(templateConfig, channel, mediaDetailsId, options = {}) {
89
- if (channel !== WHATSAPP || !templateConfig?.cards?.length) {
90
- return templateConfig;
91
- }
92
-
93
- if (!mediaDetailsId) {
94
- console.warn('mediaDetailsId is required for transformTemplateConfigWithMediaDetails');
95
- return templateConfig;
96
- }
97
-
98
- const { forceDirection = null } = options;
99
-
100
- // Detect what type of content we have
101
- const contentType = detectMediaContentType(templateConfig);
102
-
103
- if (contentType === MEDIA_CONTENT_TYPE.NONE) {
104
- console.log('No media content detected, returning original config');
105
- return templateConfig;
106
- }
107
-
108
- // Determine transformation direction
109
- let transformDirection;
110
- if (forceDirection) {
111
- transformDirection = forceDirection;
112
- console.log(`Forced transformation direction: ${transformDirection}`);
113
- } else {
114
- // Auto-detect based on content
115
- if (contentType === MEDIA_CONTENT_TYPE.URLS) {
116
- transformDirection = TRANSFORM_DIRECTION.FORWARD; // URLs → tags
117
- console.log('Detected URLs, applying forward transformation (URLs → tags)');
118
- } else if (contentType === MEDIA_CONTENT_TYPE.TAGS) {
119
- transformDirection = TRANSFORM_DIRECTION.REVERSE; // tags → URLs
120
- console.log('Detected media_content tags, applying reverse transformation (tags → URLs)');
121
- } else if (contentType === MEDIA_CONTENT_TYPE.MIXED) {
122
- transformDirection = TRANSFORM_DIRECTION.SMART; // Handle mixed content intelligently
123
- console.log('Detected mixed content, applying smart transformation');
124
- }
125
- }
126
-
127
- const newConfig = cloneDeep(templateConfig);
128
- try {
129
- console.log(`Fetching media details for ${transformDirection} transformation, ID: ${mediaDetailsId}`);
130
-
131
- // Fetch media details mapping from API
132
- const response = await getMediaDetails({ id: mediaDetailsId })
133
-
134
- // Extract the URL-to-ID mapping from the API response
135
- const urlToIdMapping = response?.response || {};
136
-
137
- if (Object.keys(urlToIdMapping).length === 0) {
138
- console.warn('No media details mapping found in API response');
139
- return newConfig;
140
- }
141
-
142
- // Create reverse mapping: ID -> URL (for reverse transformations)
143
- const idToUrlMapping = {};
144
- Object.entries(urlToIdMapping).forEach(([url, mediaId]) => {
145
- idToUrlMapping[mediaId] = url;
146
- });
147
-
148
- console.log(`Processing ${Object.keys(urlToIdMapping).length} media mappings`);
149
-
150
- // Apply transformations based on direction
151
- newConfig?.cards?.forEach(card => {
152
- if (card?.media && card?.media?.url) {
153
- const currentUrl = card.media.url;
154
-
155
- if (transformDirection === TRANSFORM_DIRECTION.FORWARD ||
156
- (transformDirection === TRANSFORM_DIRECTION.SMART && !extractMediaIdFromTag(currentUrl))) {
157
- // Forward transformation: URL → tag
158
- const mediaDetailId = urlToIdMapping[currentUrl];
159
- if (mediaDetailId) {
160
- console.log(`Forward: ${currentUrl} → {{media_content(${mediaDetailId})}}`);
161
- card.media.url = `{{media_content(${mediaDetailId})}}`;
162
- } else {
163
- console.warn(`No media detail ID found for URL: ${currentUrl}`);
164
- }
165
- } else if (transformDirection === TRANSFORM_DIRECTION.REVERSE ||
166
- (transformDirection === TRANSFORM_DIRECTION.SMART && extractMediaIdFromTag(currentUrl))) {
167
- // Reverse transformation: tag → URL
168
- const mediaId = extractMediaIdFromTag(currentUrl);
169
- if (mediaId) {
170
- const actualUrl = idToUrlMapping[mediaId];
171
- if (actualUrl) {
172
- console.log(`Reverse: {{media_content(${mediaId})}} → ${actualUrl}`);
173
- card.media.url = actualUrl;
174
- } else {
175
- console.warn(`No URL found for media ID: ${mediaId}`);
176
- }
177
- }
178
- }
179
- }
180
- });
181
-
182
- return newConfig;
183
-
184
- } catch (error) {
185
- console.error('Failed to fetch media details for transformation:', error);
186
-
187
- // Fallback: try to extract/convert what we can without API
188
- console.log('Falling back to URL extraction method');
189
- newConfig.cards.forEach(card => {
190
- if (card.media && card.media.url) {
191
- if (transformDirection === TRANSFORM_DIRECTION.FORWARD) {
192
- const extractedId = extractMediaIdFromUrl(card.media.url);
193
- if (extractedId) {
194
- card.media.url = `{{media_content(${extractedId})}}`;
195
- }
196
- }
197
- // For reverse transformation, we can't do much without the API mapping
198
- }
199
- });
200
-
201
- // cleanup the newConfig
202
- delete newConfig.accessToken;
203
-
204
- return newConfig;
205
- }
206
- }
207
-
208
- /**
209
- * Helper function to extract media ID from media_content tags
210
- * @param {string} url - The URL containing media_content tag
211
- * @returns {string|null} - Extracted media ID or null if not found
212
- */
213
- function extractMediaIdFromTag(url) {
214
- if (!url || typeof url !== 'string') {
215
- return null;
216
- }
217
-
218
- // Extract media ID from {{media_content(id)}} pattern
219
- const tagMatch = url.match(/\{\{media_content\(([^)]+)\)\}\}/);
220
- return tagMatch ? tagMatch[1] : null;
221
- }
222
-
223
- /**
224
- * Convenience function for reverse transformation: fetches media details and converts tags to URLs in one call.
225
- * This function forces reverse transformation direction. For automatic detection, use transformTemplateConfigWithMediaDetails.
226
- * @param {object} templateConfig The template configuration containing media_content tags.
227
- * @param {string} channel The channel for which to transform the config.
228
- * @param {string} mediaDetailsId The ID to use when calling getMediaDetails API.
229
- * @param {object} options Additional options for the transformation.
230
- * @returns {Promise<object>} Promise that resolves to the template config with actual URLs.
231
- */
232
- export async function convertMediaTagsToUrls(templateConfig, channel, mediaDetailsId, options = {}) {
233
- return transformTemplateConfigWithMediaDetails(templateConfig, channel, mediaDetailsId, {
234
- ...options,
235
- forceDirection: TRANSFORM_DIRECTION.REVERSE
236
- });
237
- }
238
-
239
- /**
240
- * Convenience function for forward transformation: fetches media details and converts URLs to tags in one call.
241
- * This function forces forward transformation direction. For automatic detection, use transformTemplateConfigWithMediaDetails.
242
- * @param {object} templateConfig The template configuration containing URLs.
243
- * @param {string} channel The channel for which to transform the config.
244
- * @param {string} mediaDetailsId The ID to use when calling getMediaDetails API.
245
- * @param {object} options Additional options for the transformation.
246
- * @returns {Promise<object>} Promise that resolves to the template config with media_content tags.
247
- */
248
- export async function convertUrlsToMediaTags(templateConfig, channel, mediaDetailsId, options = {}) {
249
- return transformTemplateConfigWithMediaDetails(templateConfig, channel, mediaDetailsId, {
250
- ...options,
251
- forceDirection: TRANSFORM_DIRECTION.FORWARD
252
- });
253
- }