@xiboplayer/xmds 0.7.21 → 0.7.22
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.
- package/package.json +2 -2
- package/src/schedule-parser.js +33 -2
- package/src/schedule-parser.test.js +76 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiboplayer/xmds",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.22",
|
|
4
4
|
"description": "XMDS SOAP client for Xibo CMS communication",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"./schedule-parser": "./src/schedule-parser.js"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@xiboplayer/utils": "0.7.
|
|
15
|
+
"@xiboplayer/utils": "0.7.22"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"vitest": "^4.1.2"
|
package/src/schedule-parser.js
CHANGED
|
@@ -30,6 +30,35 @@ function parseCriteria(parentEl) {
|
|
|
30
30
|
return criteria;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Parse a `<tags>` child element (if present) into a flat string array.
|
|
35
|
+
*
|
|
36
|
+
* Used by the #236 sync bridge when a schedule response exposes
|
|
37
|
+
* per-layout tags (e.g. `<tag>xp-sync-group:NAME</tag>`). Upstream
|
|
38
|
+
* Xibo CMS does NOT currently emit `<tags>` inside schedule `<layout>`
|
|
39
|
+
* entries — the authoritative source is the XLF file itself, parsed
|
|
40
|
+
* by renderer-lite.parseXlf(). This helper exists so that:
|
|
41
|
+
* 1. A forked CMS that DOES emit per-layout `<tags>` (or future
|
|
42
|
+
* upstream) gets them surfaced for free.
|
|
43
|
+
* 2. Callers can treat `layout.tags` as a stable field (empty array
|
|
44
|
+
* when absent).
|
|
45
|
+
*
|
|
46
|
+
* @param {Element} parentEl - Layout/campaign-layout element
|
|
47
|
+
* @returns {string[]} Tags parsed from a direct <tags> child, or [].
|
|
48
|
+
*/
|
|
49
|
+
function parseLayoutTags(parentEl) {
|
|
50
|
+
const tags = [];
|
|
51
|
+
for (const child of parentEl.children) {
|
|
52
|
+
if (child.tagName !== 'tags') continue;
|
|
53
|
+
for (const tagEl of child.children) {
|
|
54
|
+
if (tagEl.tagName !== 'tag') continue;
|
|
55
|
+
const text = (tagEl.textContent || '').trim();
|
|
56
|
+
if (text) tags.push(text);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return tags;
|
|
60
|
+
}
|
|
61
|
+
|
|
33
62
|
/**
|
|
34
63
|
* Parse Schedule XML response into a normalized schedule object.
|
|
35
64
|
*
|
|
@@ -121,7 +150,8 @@ export function parseScheduleResponse(xml) {
|
|
|
121
150
|
groupKey: layoutEl.getAttribute('groupKey') || null,
|
|
122
151
|
playCount: parseInt(layoutEl.getAttribute('playCount') || '0'),
|
|
123
152
|
dependants: depEls.length > 0 ? [...depEls].map(el => el.textContent) : [],
|
|
124
|
-
criteria: parseCriteria(layoutEl)
|
|
153
|
+
criteria: parseCriteria(layoutEl),
|
|
154
|
+
tags: parseLayoutTags(layoutEl) // #236 sync bridge (currently no-op upstream; forks may emit)
|
|
125
155
|
});
|
|
126
156
|
}
|
|
127
157
|
|
|
@@ -154,7 +184,8 @@ export function parseScheduleResponse(xml) {
|
|
|
154
184
|
recurrenceRepeatsOn: layoutEl.getAttribute('recurrenceRepeatsOn') || null,
|
|
155
185
|
recurrenceRange: layoutEl.getAttribute('recurrenceRange') || null,
|
|
156
186
|
dependants: depEls.length > 0 ? [...depEls].map(el => el.textContent) : [],
|
|
157
|
-
criteria: parseCriteria(layoutEl)
|
|
187
|
+
criteria: parseCriteria(layoutEl),
|
|
188
|
+
tags: parseLayoutTags(layoutEl) // #236 sync bridge (currently no-op upstream; forks may emit)
|
|
158
189
|
});
|
|
159
190
|
}
|
|
160
191
|
|
|
@@ -264,6 +264,82 @@ describe('parseScheduleResponse', () => {
|
|
|
264
264
|
expect(layout.playCount).toBe(2);
|
|
265
265
|
});
|
|
266
266
|
|
|
267
|
+
// ── Layout tag surfacing (#236 sync bridge) ───────────────────
|
|
268
|
+
//
|
|
269
|
+
// Upstream Xibo CMS does NOT currently emit <tags> as a child of
|
|
270
|
+
// <layout> in Schedule XMDS responses (only <dependants>,
|
|
271
|
+
// <criteria>). The authoritative source for layout tags is the XLF
|
|
272
|
+
// file itself, parsed by renderer-lite. These tests exercise the
|
|
273
|
+
// defensive path for forks / future upstream that DO emit <tags>,
|
|
274
|
+
// and document that the default is an empty array.
|
|
275
|
+
|
|
276
|
+
describe('layout tag surfacing (#236)', () => {
|
|
277
|
+
it('defaults tags to empty array on standalone layouts', () => {
|
|
278
|
+
const xml = `<schedule>
|
|
279
|
+
<layout file="100.xlf" fromdt="2025-01-01 00:00:00" todt="2025-12-31 23:59:59"
|
|
280
|
+
scheduleid="1" priority="0"/>
|
|
281
|
+
</schedule>`;
|
|
282
|
+
const result = parseScheduleResponse(xml);
|
|
283
|
+
expect(result.layouts[0].tags).toEqual([]);
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it('defaults tags to empty array on campaign layouts', () => {
|
|
287
|
+
const xml = `<schedule>
|
|
288
|
+
<campaign id="c1" priority="5" fromdt="2025-01-01 00:00:00" todt="2025-12-31 23:59:59"
|
|
289
|
+
scheduleid="30">
|
|
290
|
+
<layout file="500.xlf"/>
|
|
291
|
+
</campaign>
|
|
292
|
+
</schedule>`;
|
|
293
|
+
const result = parseScheduleResponse(xml);
|
|
294
|
+
expect(result.campaigns[0].layouts[0].tags).toEqual([]);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('parses <tags> when present on standalone layouts', () => {
|
|
298
|
+
const xml = `<schedule>
|
|
299
|
+
<layout file="700.xlf" fromdt="2025-01-01 00:00:00" todt="2025-12-31 23:59:59"
|
|
300
|
+
scheduleid="7" priority="0">
|
|
301
|
+
<tags>
|
|
302
|
+
<tag>xp-sync-group:lobby-wall</tag>
|
|
303
|
+
<tag>smil-imported</tag>
|
|
304
|
+
</tags>
|
|
305
|
+
</layout>
|
|
306
|
+
</schedule>`;
|
|
307
|
+
const result = parseScheduleResponse(xml);
|
|
308
|
+
expect(result.layouts[0].tags).toEqual([
|
|
309
|
+
'xp-sync-group:lobby-wall',
|
|
310
|
+
'smil-imported',
|
|
311
|
+
]);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('parses <tags> when present on campaign layouts', () => {
|
|
315
|
+
const xml = `<schedule>
|
|
316
|
+
<campaign id="c1" priority="5" fromdt="2025-01-01 00:00:00" todt="2025-12-31 23:59:59"
|
|
317
|
+
scheduleid="30">
|
|
318
|
+
<layout file="500.xlf">
|
|
319
|
+
<tags><tag>xp-sync-group:atrium</tag></tags>
|
|
320
|
+
</layout>
|
|
321
|
+
</campaign>
|
|
322
|
+
</schedule>`;
|
|
323
|
+
const result = parseScheduleResponse(xml);
|
|
324
|
+
expect(result.campaigns[0].layouts[0].tags).toEqual(['xp-sync-group:atrium']);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('skips empty and whitespace-only <tag> elements', () => {
|
|
328
|
+
const xml = `<schedule>
|
|
329
|
+
<layout file="800.xlf" fromdt="2025-01-01 00:00:00" todt="2025-12-31 23:59:59"
|
|
330
|
+
scheduleid="8" priority="0">
|
|
331
|
+
<tags>
|
|
332
|
+
<tag> xp-sync-group:lobby </tag>
|
|
333
|
+
<tag></tag>
|
|
334
|
+
<tag> </tag>
|
|
335
|
+
</tags>
|
|
336
|
+
</layout>
|
|
337
|
+
</schedule>`;
|
|
338
|
+
const result = parseScheduleResponse(xml);
|
|
339
|
+
expect(result.layouts[0].tags).toEqual(['xp-sync-group:lobby']);
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
267
343
|
it('should use consistent lowercase casing for fromdt/todt/scheduleid on overlays', () => {
|
|
268
344
|
const xml = `<schedule>
|
|
269
345
|
<overlays>
|