From d0d1d157f4a0f3795e8990a6ab0817e5f1602868 Mon Sep 17 00:00:00 2001 From: Enno Gotthold Date: Tue, 5 Nov 2024 14:54:01 +0100 Subject: [PATCH] XMLRPC-Client: Remove the need for custom list and map type --- .../src/lib/deserializer.spec.ts | 18 ++++++---- .../src/lib/serializer.spec.ts | 22 ++++++++++++ .../typescript-xmlrpc/src/lib/serializer.ts | 20 +++++++++-- .../src/lib/typescript-xmlrpc.service.spec.ts | 6 ++-- .../src/lib/typescript-xmlrpc.service.ts | 19 +++------- projects/typescript-xmlrpc/src/lib/utils.ts | 35 +++++++++++++++++++ .../typescript-xmlrpc/src/lib/xmlrpc-types.ts | 9 +++-- 7 files changed, 103 insertions(+), 26 deletions(-) create mode 100644 projects/typescript-xmlrpc/src/lib/utils.ts diff --git a/projects/typescript-xmlrpc/src/lib/deserializer.spec.ts b/projects/typescript-xmlrpc/src/lib/deserializer.spec.ts index d54e1544..3611e056 100644 --- a/projects/typescript-xmlrpc/src/lib/deserializer.spec.ts +++ b/projects/typescript-xmlrpc/src/lib/deserializer.spec.ts @@ -1,6 +1,7 @@ import { TestBed } from '@angular/core/testing'; import { deserialize } from './deserializer'; import { applicationError } from './constants'; +import { MethodFault, MethodResponse } from './xmlrpc-types'; describe('Deserializer', () => { beforeEach(() => { @@ -11,28 +12,32 @@ describe('Deserializer', () => { // eslint-disable-next-line max-len const goodInput = `South Dakota`; const result = deserialize(goodInput); - expect(result).toEqual({ value: 'South Dakota' }); + const expected: MethodResponse = { value: 'South Dakota' }; + expect(result).toEqual(expected); }); it('method response with int', () => { // eslint-disable-next-line max-len const goodInput = `10`; const result = deserialize(goodInput); - expect(result).toEqual({ value: 10 }); + const expected: MethodResponse = { value: 10 }; + expect(result).toEqual(expected); }); it('method response with true bool', () => { // eslint-disable-next-line max-len const goodInput = `1`; const result = deserialize(goodInput); - expect(result).toEqual({ value: true }); + const expected: MethodResponse = { value: true }; + expect(result).toEqual(expected); }); it('method response with false bool', () => { // eslint-disable-next-line max-len const goodInput = `0`; const result = deserialize(goodInput); - expect(result).toEqual({ value: false }); + const expected: MethodResponse = { value: false }; + expect(result).toEqual(expected); }); it('method response with invalid bool', () => { @@ -46,10 +51,11 @@ describe('Deserializer', () => { // eslint-disable-next-line max-len const goodInput = `faultCode4faultStringToo many parameters.`; const result = deserialize(goodInput); - expect(result).toEqual({ + const expected: MethodFault = { faultCode: 4, faultString: 'Too many parameters.', - }); + }; + expect(result).toEqual(expected); }); it('process garbage like server errors', () => { diff --git a/projects/typescript-xmlrpc/src/lib/serializer.spec.ts b/projects/typescript-xmlrpc/src/lib/serializer.spec.ts index 5a7d3f8a..edb84e0a 100644 --- a/projects/typescript-xmlrpc/src/lib/serializer.spec.ts +++ b/projects/typescript-xmlrpc/src/lib/serializer.spec.ts @@ -1,5 +1,6 @@ import { TestBed } from '@angular/core/testing'; import { serializeMethodCall } from './serializer'; +import { XmlRpcTypes } from './xmlrpc-types'; describe('serializeMethodCall', () => { beforeEach(() => { @@ -52,6 +53,14 @@ describe('serializeMethodCall', () => { }); it('function with array parameter', () => { + // eslint-disable-next-line max-len + const expectedResult = `function_nameelem1elem2`; + expect(serializeMethodCall('function_name', [['elem1', 'elem2']])).toBe( + expectedResult, + ); + }); + + it('function with xmlrpc array parameter', () => { // eslint-disable-next-line max-len const expectedResult = `function_nameelem1elem2`; expect( @@ -71,6 +80,19 @@ describe('serializeMethodCall', () => { ); }); + it('function with map parameter', () => { + // eslint-disable-next-line max-len + const expectedResult = `function_namedata10data2`; + expect( + serializeMethodCall('function_name', [ + new Map([ + ['data1', 0], + ['data2', ''], + ]), + ]), + ).toBe(expectedResult); + }); + it('function with struct parameter', () => { // eslint-disable-next-line max-len const expectedResult = `function_namedata10data2`; diff --git a/projects/typescript-xmlrpc/src/lib/serializer.ts b/projects/typescript-xmlrpc/src/lib/serializer.ts index c82ad1c5..579fbb4a 100644 --- a/projects/typescript-xmlrpc/src/lib/serializer.ts +++ b/projects/typescript-xmlrpc/src/lib/serializer.ts @@ -1,6 +1,7 @@ import { create } from 'xmlbuilder2'; import { DateFormatter } from './date_formatter'; import { XMLBuilder } from 'xmlbuilder2/lib/interfaces'; +import { Utils } from './utils'; import { XmlRpcArray, XmlRpcStruct, XmlRpcTypes } from './xmlrpc-types'; /** @@ -59,14 +60,29 @@ function serializeValue(value: XmlRpcTypes, xml: XMLBuilder): void { appendBuffer(value as ArrayBuffer, xml); break; } else { - if ('data' in value) { + if (Utils.instanceOfXmlRpcArray(value)) { appendArray( value as XmlRpcArray, xml.ele('value').ele('array').ele('data'), ); break; } - if ('members' in value) { + if (Array.isArray(value)) { + appendArray( + { data: value }, + xml.ele('value').ele('array').ele('data'), + ); + break; + } + if (value instanceof Map) { + const xmlrpcstruct = { members: [] }; + for (let [key, val] of value) { + xmlrpcstruct.members.push({ name: key, value: val }); + } + appendStruct(xmlrpcstruct, xml.ele('value').ele('struct')); + break; + } + if (Utils.instanceOfXmlRpcStruct(value)) { appendStruct(value as XmlRpcStruct, xml.ele('value').ele('struct')); break; } diff --git a/projects/typescript-xmlrpc/src/lib/typescript-xmlrpc.service.spec.ts b/projects/typescript-xmlrpc/src/lib/typescript-xmlrpc.service.spec.ts index e702dd89..8ba5cd70 100644 --- a/projects/typescript-xmlrpc/src/lib/typescript-xmlrpc.service.spec.ts +++ b/projects/typescript-xmlrpc/src/lib/typescript-xmlrpc.service.spec.ts @@ -5,6 +5,7 @@ import { HttpClientTestingModule, HttpTestingController, } from '@angular/common/http/testing'; +import { MethodResponse } from './xmlrpc-types'; describe('AngularXmlrpcService', () => { let service: AngularXmlrpcService; @@ -39,8 +40,9 @@ describe('AngularXmlrpcService', () => { const testData = `3.2.1`; service.configureService(new URL('http://localhost/cobbler_api')); const callObservable = service.methodCall('version'); + const expected: MethodResponse = { value: '3.2.1' }; callObservable.subscribe((value) => { - expect(value).toEqual({ value: '3.2.1' }); + expect(value).toEqual(expected); done(); }); const req = httpTestingController.expectOne('http://localhost/cobbler_api'); @@ -84,7 +86,7 @@ describe('AngularXmlrpcService static methods', () => { ).toBe(parameter.resultResponse); }); - it('instanceOfMethodFaullt', () => { + it('instanceOfMethodFault', () => { expect(AngularXmlrpcService.instanceOfMethodFault(parameter.data)).toBe( parameter.resultFault, ); diff --git a/projects/typescript-xmlrpc/src/lib/typescript-xmlrpc.service.ts b/projects/typescript-xmlrpc/src/lib/typescript-xmlrpc.service.ts index fe33bfad..77cb15eb 100644 --- a/projects/typescript-xmlrpc/src/lib/typescript-xmlrpc.service.ts +++ b/projects/typescript-xmlrpc/src/lib/typescript-xmlrpc.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { serializeMethodCall } from './serializer'; +import { Utils } from './utils'; import { MethodFault, MethodResponse, @@ -25,29 +26,19 @@ export class AngularXmlrpcService { private headers: object; static instanceOfMethodResponse(object: object): object is MethodResponse { - return 'value' in object; + return Utils.instanceOfMethodResponse(object); } static instanceOfMethodFault(object: object): object is MethodFault { - return 'faultCode' in object && 'faultString' in object; + return Utils.instanceOfMethodFault(object); } static instanceOfXmlRpcStruct(object: XmlRpcTypes): object is XmlRpcStruct { - return ( - object !== null && - typeof object === 'object' && - Object.keys(object).length === 1 && - 'members' in object - ); + return Utils.instanceOfXmlRpcStruct(object); } static instanceOfXmlRpcArray(object: XmlRpcTypes): object is XmlRpcArray { - return ( - object !== null && - typeof object === 'object' && - Object.keys(object).length === 1 && - 'data' in object - ); + return Utils.instanceOfXmlRpcArray(object); } constructor(http: HttpClient) { diff --git a/projects/typescript-xmlrpc/src/lib/utils.ts b/projects/typescript-xmlrpc/src/lib/utils.ts new file mode 100644 index 00000000..977472a1 --- /dev/null +++ b/projects/typescript-xmlrpc/src/lib/utils.ts @@ -0,0 +1,35 @@ +import { + MethodFault, + MethodResponse, + XmlRpcArray, + XmlRpcStruct, + XmlRpcTypes, +} from './xmlrpc-types'; + +export class Utils { + static instanceOfMethodResponse(object: object): object is MethodResponse { + return 'value' in object; + } + + static instanceOfMethodFault(object: object): object is MethodFault { + return 'faultCode' in object && 'faultString' in object; + } + + static instanceOfXmlRpcStruct(object: XmlRpcTypes): object is XmlRpcStruct { + return ( + object !== null && + typeof object === 'object' && + Object.keys(object).length === 1 && + 'members' in object + ); + } + + static instanceOfXmlRpcArray(object: XmlRpcTypes): object is XmlRpcArray { + return ( + object !== null && + typeof object === 'object' && + Object.keys(object).length === 1 && + 'data' in object + ); + } +} diff --git a/projects/typescript-xmlrpc/src/lib/xmlrpc-types.ts b/projects/typescript-xmlrpc/src/lib/xmlrpc-types.ts index d5c0285b..c9f7201d 100644 --- a/projects/typescript-xmlrpc/src/lib/xmlrpc-types.ts +++ b/projects/typescript-xmlrpc/src/lib/xmlrpc-types.ts @@ -5,8 +5,13 @@ export type XmlRpcTypes = | Date | ArrayBuffer | XmlRpcStruct - | XmlRpcArray; -export type MethodResponse = Param; + | Map + | XmlRpcArray + | Array; + +export interface MethodResponse { + value: XmlRpcTypes; +} export interface MethodFault { faultCode: number;