Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update some packages and apply needed compliance to xmlParser Changes (src:@theimo1221) #185

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading