Skip to content

Commit

Permalink
Update some packages and apply needed compliance to xmlParser Changes
Browse files Browse the repository at this point in the history
  • Loading branch information
theimo1221 authored and simojenki committed Jan 25, 2024
1 parent fa60a2e commit 509cc32
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 53 deletions.
110 changes: 75 additions & 35 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"homepage": "https://svrooij.io/node-sonos-ts/",
"devDependencies": {
"@types/chai": "^4.2.16",
"@types/debug": "^4.1.5",
"@types/debug": "^4.1.8",
"@types/jest": "^26.0.15",
"@types/node": "^16.11.7",
"@types/node-fetch": "^2.5.10",
Expand All @@ -53,10 +53,10 @@
"typescript": "^3.8.5"
},
"dependencies": {
"debug": "4.3.1",
"fast-xml-parser": "3.19.0",
"debug": "4.3.4",
"fast-xml-parser": "4.2.4",
"guid-typescript": "^1.0.9",
"html-entities": "^2.3.2",
"html-entities": "^2.3.5",
"node-fetch": "^2.6.1",
"typed-emitter": "^1.3.1",
"ws": "^8.12.1"
Expand Down
15 changes: 12 additions & 3 deletions src/helpers/xml-helper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { parse } from 'fast-xml-parser';
import { encode, decode } from 'html-entities';
import { XMLParser, XMLValidator } from 'fast-xml-parser';

export default class XmlHelper {
/**
Expand Down Expand Up @@ -41,13 +41,18 @@ export default class XmlHelper {
*
* @static
* @param {string} encodedXml Encoded Xml string
* @param attributeNamePrefix
* @returns {*} a parsed Object of the XML string
* @memberof XmlHelper
*/
static DecodeAndParseXml(encodedXml: unknown, attributeNamePrefix = '_'): unknown {
const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix });
if (typeof encodedXml === 'string' && encodedXml !== '' && XMLValidator.validate(encodedXml)) {
return parser.parse(encodedXml as string);
}
const decoded = XmlHelper.DecodeXml(encodedXml);
if (typeof decoded === 'undefined') return undefined;
return parse(decoded, { ignoreAttributes: false, attributeNamePrefix });
return parser.parse(decoded);
}

/**
Expand All @@ -59,8 +64,12 @@ export default class XmlHelper {
* @memberof XmlHelper
*/
static DecodeAndParseXmlNoNS(encodedXml: unknown, attributeNamePrefix = '_'): unknown {
const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix });
if (typeof encodedXml === 'string' && encodedXml !== '' && XMLValidator.validate(encodedXml)) {
return parser.parse(encodedXml as string);
}
const decoded = XmlHelper.DecodeXml(encodedXml);
return decoded ? parse(decoded, { ignoreAttributes: false, ignoreNameSpace: true, attributeNamePrefix }) : undefined;
return decoded ? parser.parse(decoded) : undefined;
}

/**
Expand Down
6 changes: 4 additions & 2 deletions src/musicservices/smapi-client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fetch, { Request } from 'node-fetch';
import { parse } from 'fast-xml-parser';
import debug, { Debugger } from 'debug';

import { XMLParser } from 'fast-xml-parser';
import SmapiError from './smapi-error';
import ArrayHelper from '../helpers/array-helper';

Expand Down Expand Up @@ -108,6 +108,8 @@ export class SmapiClient {

private authToken?: string;

private readonly parser: XMLParser = new XMLParser();

protected get debug(): Debugger {
if (this.debugger === undefined) this.debugger = debug(`sonos:smapi:${this.options.name}`);
return this.debugger;
Expand Down Expand Up @@ -265,7 +267,7 @@ export class SmapiClient {
// throw new Error(`Http status ${response.status} (${response.statusText})`);
// }

const result = parse(await response.text(), { ignoreNameSpace: true });
const result = this.parser.parse(await response.text());
if (!result || !result.Envelope || !result.Envelope.Body) {
this.debug('Invalid response for %s %o', action, result);
throw new Error(`Invalid response for ${action}: ${result}`);
Expand Down
17 changes: 10 additions & 7 deletions src/services/base-service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fetch, { Request, Response } from 'node-fetch';

import { parse } from 'fast-xml-parser';
import { XMLParser, XMLValidator } from 'fast-xml-parser';
import { Guid } from 'guid-typescript';
import { EventEmitter } from 'events';
import debug, { Debugger } from 'debug';
Expand Down Expand Up @@ -29,6 +29,8 @@ import AsyncHelper from '../helpers/async-helper';
export default abstract class BaseService <TServiceEvent> {
protected readonly host: string;

protected readonly parser: XMLParser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '' });

protected readonly port: number;

private debugger?: Debugger;
Expand Down Expand Up @@ -261,7 +263,7 @@ export default abstract class BaseService <TServiceEvent> {
? await response.text()
: await this.handleErrorResponse<string>(action, response);

const result = parse(responseText);
const result = this.parser.parse(responseText);
if (!result || !result['s:Envelope']) {
this.debug('Invalid response for %s %o', action, result);
throw new Error(`Invalid response for ${action}: ${result}`);
Expand All @@ -281,7 +283,7 @@ export default abstract class BaseService <TServiceEvent> {
private async handleErrorResponse<TResponse>(action: string, response: Response): Promise<TResponse> {
const responseText = await response.text();
if (responseText !== '') {
const errorResponse = parse(responseText);
const errorResponse = this.parser.parse(responseText);
if (errorResponse['s:Envelope'] && errorResponse['s:Envelope']['s:Body'] && errorResponse['s:Envelope']['s:Body']['s:Fault'] !== undefined) {
const error = errorResponse['s:Envelope']['s:Body']['s:Fault'];
this.debug('Sonos error on %s %o', action, error);
Expand Down Expand Up @@ -316,7 +318,7 @@ export default abstract class BaseService <TServiceEvent> {

protected parseValue(name: string, input: unknown, expectedType: string): Track | string | boolean | number | unknown {
if (expectedType === 'Track | string' && typeof input === 'string') {
if (input.startsWith('&lt;')) {
if (input.startsWith('&lt;') || (input.startsWith('<') && XMLValidator.validate(input))) {
return MetadataHelper.ParseDIDLTrack(XmlHelper.DecodeAndParseXml(input), this.host, this.port);
}
return undefined; // undefined is more appropriate, but that would be a breaking change.
Expand Down Expand Up @@ -524,7 +526,7 @@ export default abstract class BaseService <TServiceEvent> {
*/
public ParseEvent(xml: string): void {
this.debug('Got event');
const rawBody = parse(xml, { attributeNamePrefix: '', ignoreNameSpace: true }).propertyset.property;
const rawBody = this.parser.parse(xml)['e:propertyset']['e:property'];
this.Events.emit(ServiceEvents.Unprocessed, rawBody);
if (rawBody.LastChange) {
const rawEventWrapper = XmlHelper.DecodeAndParseXmlNoNS(rawBody.LastChange, '') as any;
Expand All @@ -544,7 +546,8 @@ export default abstract class BaseService <TServiceEvent> {
}

protected ResolveEventPropertyValue(name: string, originalValue: unknown, type: string): unknown {
if (typeof originalValue === 'string' && originalValue.startsWith('&lt;')) {
if (typeof originalValue === 'string' && (originalValue.startsWith('&lt;')
|| (originalValue.startsWith('<') && XMLValidator.validate(originalValue)))) {
if (name.endsWith('MetaData')) {
return MetadataHelper.ParseDIDLTrack(XmlHelper.DecodeAndParseXml(originalValue), this.host, this.port);
}
Expand Down Expand Up @@ -579,7 +582,7 @@ export default abstract class BaseService <TServiceEvent> {

if (Object.keys(output).length === 0) {
const entries = Object.entries(input);
if (entries.length === 1) {
if (entries.length === 1 || (entries.length === 2 && entries[1][0] === 'xmlns')) {
return this.cleanEventLastChange(entries[0][1]);
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/sonos-device.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { EventEmitter } from 'events';
import TypedEmitter from 'typed-emitter';
import fetch from 'node-fetch';
import { parse } from 'fast-xml-parser';
import { XMLParser } from 'fast-xml-parser';
import WebSocket from 'ws';
import SonosDeviceBase from './sonos-device-base';
import {
Expand Down Expand Up @@ -36,6 +36,8 @@ import SonosDeviceNotifications from './sonos-notification-two';
export default class SonosDevice extends SonosDeviceBase {
private name: string | undefined;

private readonly parser: XMLParser = new XMLParser();

private groupName: string | undefined;

private coordinator: SonosDevice | undefined;
Expand Down Expand Up @@ -201,7 +203,7 @@ export default class SonosDevice extends SonosDeviceBase {
}
throw new Error(`Loading device description failed ${response.status} ${response.statusText}`);
});
const { root: { device } } = parse(resp);
const { root: { device } } = this.parser.parse(resp);
return {
manufacturer: device.manufacturer,
modelNumber: device.modelNumber,
Expand Down

0 comments on commit 509cc32

Please sign in to comment.