Skip to content

Commit 28367ea

Browse files
committed
wip
1 parent aa5ac9d commit 28367ea

File tree

14 files changed

+454
-181
lines changed

14 files changed

+454
-181
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { getHandleError } from '../lib'
2+
import { getParseMosTypes } from '../parseMosTypes'
3+
4+
describe('lib', () => {
5+
test('getHandleError types', () => {
6+
const { mosString128 } = getParseMosTypes(true)
7+
const handleError = getHandleError('unit test')
8+
9+
handleError(mosString128.createRequired, 'test', 'path')
10+
11+
// @ts-expect-error number is not a valid XML type, only string
12+
handleError(mosString128.createRequired, 1234, 'path')
13+
})
14+
})
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { AnyXMLValue, AnyXMLValueSingular } from '@mos-connection/model'
2+
import { getParseMosTypes } from '../parseMosTypes'
3+
4+
describe('parseMosTypes', () => {
5+
test('createRequired types', () => {
6+
const parseMosTypes = getParseMosTypes(true)
7+
8+
const testTypes = (xmlValue: AnyXMLValue, xmlValueSingular: AnyXMLValueSingular) => {
9+
parseMosTypes.mosString128.createRequired(xmlValue)
10+
parseMosTypes.mosTime.createRequired(xmlValue)
11+
parseMosTypes.mosDuration.createRequired(xmlValue)
12+
13+
parseMosTypes.mosString128.createRequired(xmlValueSingular)
14+
parseMosTypes.mosTime.createRequired(xmlValueSingular)
15+
parseMosTypes.mosDuration.createRequired(xmlValueSingular)
16+
17+
// @ts-expect-error bad type
18+
parseMosTypes.mosString128.createRequired({ somethingNonXml: 1234 })
19+
// @ts-expect-error bad type
20+
parseMosTypes.mosTime.createRequired({ somethingNonXml: 1234 })
21+
// @ts-expect-error bad type
22+
parseMosTypes.mosDuration.createRequired({ somethingNonXml: 1234 })
23+
}
24+
try {
25+
testTypes('', '')
26+
} catch {
27+
// Ignore any errors
28+
}
29+
30+
expect(1).toEqual(1)
31+
})
32+
test('createOptional types', () => {
33+
const parseMosTypes = getParseMosTypes(true)
34+
35+
const testTypes = (xmlValue: AnyXMLValue, xmlValueSingular: AnyXMLValueSingular) => {
36+
parseMosTypes.mosString128.createOptional(xmlValue)
37+
parseMosTypes.mosTime.createOptional(xmlValue)
38+
parseMosTypes.mosDuration.createOptional(xmlValue)
39+
40+
parseMosTypes.mosString128.createOptional(xmlValueSingular)
41+
parseMosTypes.mosTime.createOptional(xmlValueSingular)
42+
parseMosTypes.mosDuration.createOptional(xmlValueSingular)
43+
44+
// @ts-expect-error bad type
45+
parseMosTypes.mosString128.createOptional({ somethingNonXml: 1234 })
46+
// @ts-expect-error bad type
47+
parseMosTypes.mosTime.createOptional({ somethingNonXml: 1234 })
48+
// @ts-expect-error bad type
49+
parseMosTypes.mosDuration.createOptional({ somethingNonXml: 1234 })
50+
}
51+
try {
52+
testTypes('', '')
53+
} catch {
54+
// Ignore any errors
55+
}
56+
57+
expect(1).toEqual(1)
58+
})
59+
})

packages/helper/src/mosModel/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export * from './MosMessage'
22

3-
export { AnyXML } from './lib'
3+
export { AnyXMLObject as AnyXML } from './lib'
44
export * from './profile0'
55
export * from './profile1'
66
export * from './profile2'

packages/helper/src/mosModel/lib.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ export function numberOrUndefined(value: unknown): number | undefined {
2020
return num
2121
}
2222

23-
/** Just a generic type to use instead of "any", for xml objects */
24-
export type AnyXML = { [key: string]: any }
23+
export { AnyXMLObject, AnyXMLValue, AnyXMLValueSingular } from '@mos-connection/model'
2524

2625
/** Return true if the object has a property */
2726
export function has(obj: unknown, property: string): boolean {
@@ -43,3 +42,8 @@ export function getHandleError(basePath: string): <V, R>(func: (val: V) => R, va
4342

4443
return handleError
4544
}
45+
46+
export function ensureArray<T>(v: T | T[]): T[] {
47+
if (typeof v === 'object' && Array.isArray(v)) return v
48+
else return [v]
49+
}

packages/helper/src/mosModel/parseMosTypes.ts

Lines changed: 87 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,48 @@
11
import { MosTypes, getMosTypes, MosType } from '@mos-connection/model'
2+
import { AnyXMLValue, AnyXMLValueSingular } from './lib'
23

34
export function getParseMosTypes(strict: boolean): MosParseTypes {
45
const mosTypes = getMosTypes(strict)
56

7+
const specialMosTypes = getSpecialMosTypes(strict)
68
return {
79
strict: mosTypes.strict,
810
mosString128: wrapParseMethods(mosTypes.mosString128, strict),
911
mosDuration: wrapParseMethods(mosTypes.mosDuration, strict),
1012
mosTime: wrapParseMethods(mosTypes.mosTime, strict),
13+
14+
string: wrapParseMethods(specialMosTypes.string, strict),
15+
stringEnum: wrapParseMethods(specialMosTypes.stringEnum, strict),
1116
}
1217
}
1318
type MosParseTypes = {
1419
[key in keyof MosTypes]: MosTypes[key] extends MosType<infer Serialized, infer Value, infer CreateValue>
1520
? MosTypeParse<Serialized, Value, CreateValue>
1621
: MosTypes[key]
22+
} & {
23+
string: MosTypeParse<string, string, AnyXMLValue>
24+
stringEnum: MosTypeParse<any, string, { enum: { [key: string]: string }; value: AnyXMLValue }>
1725
}
1826
interface MosTypeParse<Serialized, Value, CreateValue> extends Omit<MosType<Serialized, Value, CreateValue>, 'create'> {
1927
/**
2028
* Used to parse data that is optional.
2129
* If the data is missing, undefined is returned.
2230
*/
23-
createOptional: (anyValue: CreateValue) => Serialized | undefined
31+
createOptional: (anyValue: CreateValue | AnyXMLValue) => Serialized | undefined
2432
/**
2533
* Used to parse data that is required.
2634
* If in strict mode, the data must be present and parsable, otherwise an error is thrown.
2735
* If not in strict mode, a fallback value will be used.
2836
*/
29-
createRequired: (anyValue: CreateValue) => Serialized
37+
createRequired: (anyValue: CreateValue | AnyXMLValue) => Serialized
3038
}
3139

3240
function wrapParseMethods<Serialized, Value, CreateValue>(
3341
mosType: MosType<Serialized, Value, CreateValue>,
3442
strict: boolean
3543
): MosTypeParse<Serialized, Value, CreateValue> {
3644
return {
37-
createOptional: wrapParseMethodCreateOptional(mosType),
45+
createOptional: wrapParseMethodCreateOptional(mosType, strict),
3846
createRequired: wrapParseMethodCreateRequired(mosType, strict),
3947
validate: mosType.validate,
4048
valueOf: mosType.valueOf,
@@ -43,9 +51,10 @@ function wrapParseMethods<Serialized, Value, CreateValue>(
4351
} as MosTypeParse<Serialized, Value, CreateValue>
4452
}
4553
function wrapParseMethodCreateOptional<Serialized, Value, CreateValue>(
46-
mosType: MosType<Serialized, Value, CreateValue>
54+
mosType: MosType<Serialized, Value, CreateValue>,
55+
strict: boolean
4756
): MosTypeParse<Serialized, Value, CreateValue>['createOptional'] {
48-
return parseOptional(mosType.create)
57+
return parseOptional(mosType.create, strict)
4958
}
5059
function wrapParseMethodCreateRequired<Serialized, Value, CreateValue>(
5160
mosType: MosType<Serialized, Value, CreateValue>,
@@ -54,24 +63,35 @@ function wrapParseMethodCreateRequired<Serialized, Value, CreateValue>(
5463
return parseRequired(mosType.create, mosType.fallback, strict)
5564
}
5665

57-
export function parseOptional<V, R>(parser: (value: V) => R): (value: V) => R | undefined {
66+
export function parseOptional<V, R>(
67+
parser: (value: V) => R,
68+
strict: boolean
69+
): (value: V | AnyXMLValue) => R | undefined {
5870
return (value: any) => {
5971
// handle empty string:
6072
if (typeof value === 'string' && !value.trim()) value = undefined
6173
// handle empty object (can happen when parsing an empty xml tag):
6274
if (typeof value === 'object' && Object.keys(value).length === 0) value = undefined
6375

76+
value = ensureSingular(value, strict)
77+
6478
if (value === undefined) return undefined
6579
return parser(value)
6680
}
6781
}
68-
export function parseRequired<V, R>(parser: (value: V) => R, fallback: () => R, strict: boolean): (value: V) => R {
82+
export function parseRequired<V, R>(
83+
parser: (value: V) => R,
84+
fallback: () => R,
85+
strict: boolean
86+
): (value: V | AnyXMLValue) => R {
6987
return (value: any) => {
7088
// handle empty string:
7189
if (typeof value === 'string' && !value.trim()) value = undefined
7290
// handle empty object (can happen when parsing an empty xml tag):
7391
if (typeof value === 'object' && Object.keys(value).length === 0) value = undefined
7492

93+
value = ensureSingular(value, strict)
94+
7595
if (value === undefined) {
7696
// Something might be wrong. value is undefined, but should not be (?)
7797
if (strict) {
@@ -85,3 +105,63 @@ export function parseRequired<V, R>(parser: (value: V) => R, fallback: () => R,
85105
}
86106
}
87107
}
108+
109+
function ensureSingular(value: AnyXMLValue, strict: boolean): AnyXMLValueSingular {
110+
if (typeof value === 'object') {
111+
if (Array.isArray(value)) {
112+
if (value.length === 0) return undefined
113+
if (value.length > 1) {
114+
if (strict)
115+
throw new Error(`Expected only one value, got ${value.length} values: ${JSON.stringify(value)}`)
116+
else return undefined
117+
}
118+
119+
return ensureSingular(value[0], strict)
120+
} else {
121+
if (strict) throw new Error(`Expected only one value, got object: ${JSON.stringify(value)}`)
122+
else return undefined
123+
}
124+
} else {
125+
return value
126+
}
127+
}
128+
129+
function getSpecialMosTypes(strict: boolean) {
130+
const string: MosType<string, string, AnyXMLValue> = {
131+
create: (anyValue: AnyXMLValue) => {
132+
if (typeof anyValue !== 'string') throw new Error(`Expected a string, got: "${anyValue}"`)
133+
return anyValue
134+
},
135+
validate: (_value: string) => true,
136+
valueOf: (value: string) => value,
137+
stringify: (value: string) => value,
138+
is: (value: string | any): value is string => typeof value !== 'string',
139+
fallback: () => '',
140+
}
141+
const stringEnum: MosType<string, string, { enum: { [key: string]: string }; value: AnyXMLValue }> = {
142+
create: (createValue) => {
143+
if (!createValue.enum) throw new Error(`Expected an object with an "enum" key, got: "${createValue}"`)
144+
145+
const key = `${createValue.value}`
146+
if (!key) throw new Error(`Expected a value, got: "${createValue.value}"`)
147+
148+
if (createValue.enum[key] === undefined) {
149+
if (strict) {
150+
throw new Error(`Unknown value, got: "${key}", possible values: ${Object.keys(createValue.enum)}`)
151+
} else return ''
152+
}
153+
154+
return key
155+
},
156+
validate: (_value: string) => true,
157+
valueOf: (value: string) => value,
158+
stringify: (value: string) => value,
159+
is: (value: string | any): value is string => typeof value !== 'string',
160+
fallback: () => '',
161+
}
162+
163+
return {
164+
string,
165+
stringEnum,
166+
}
167+
}

packages/helper/src/mosModel/profile0/xmlConversion.ts

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
import * as XMLBuilder from 'xmlbuilder'
2-
import { IMOSListMachInfo, IMOSString128 } from '@mos-connection/model'
3-
import { AnyXML, getHandleError } from '../lib'
2+
import {
3+
AnyXMLValue,
4+
IMOSDefaultActiveX,
5+
IMOSListMachInfo,
6+
IMOSListMachInfoDefaultActiveXMode,
7+
IMOSString128,
8+
} from '@mos-connection/model'
9+
import { AnyXMLObject, ensureArray, getHandleError } from '../lib'
410
import { addTextElementInternal } from '../../utils/Utils'
511
import { getParseMosTypes, parseOptional } from '../parseMosTypes'
612

713
/* eslint-disable @typescript-eslint/no-namespace */
814
export namespace XMLMosIDs {
9-
export function fromXML(xml: AnyXML, strict: boolean): IMOSString128[] {
15+
export function fromXML(xml: AnyXMLObject, strict: boolean): IMOSString128[] {
1016
const mosTypes = getParseMosTypes(strict)
1117

1218
const arr: Array<IMOSString128> = []
@@ -22,7 +28,7 @@ export namespace XMLMosIDs {
2228

2329
/* eslint-disable @typescript-eslint/no-namespace */
2430
export namespace XMLMosListMachInfo {
25-
export function fromXML(xml: AnyXML, strict: boolean): IMOSListMachInfo {
31+
export function fromXML(xml: AnyXMLObject, strict: boolean): IMOSListMachInfo {
2632
const { mosString128, mosTime } = getParseMosTypes(strict)
2733

2834
const handleError = getHandleError('listMachInfo')
@@ -43,11 +49,8 @@ export namespace XMLMosListMachInfo {
4349
xml.supportedProfiles,
4450
'supportedProfiles'
4551
),
46-
defaultActiveX: handleError(
47-
parseOptional((v) => v),
48-
xml.defaultActiveX,
49-
'defaultActiveX'
50-
),
52+
defaultActiveX: XMLDefaultActiveX.fromXML(xml.defaultActiveX, strict),
53+
5154
mosExternalMetaData: handleError((v) => v, xml.mosExternalMetaData, 'mosExternalMetaData'),
5255
}
5356

@@ -65,6 +68,7 @@ export namespace XMLMosListMachInfo {
6568
if (info.opTime) addTextElementInternal(xmlListMachInfo, 'opTime', info.opTime, undefined, strict)
6669
addTextElementInternal(xmlListMachInfo, 'mosRev', info.mosRev, undefined, strict)
6770

71+
XMLDefaultActiveX.toXML(xmlListMachInfo, info.defaultActiveX, strict)
6872
// TODO: the supportedProfiles should be changed to an Array
6973

7074
const xmlSupportedProfiles = XMLBuilder.create('supportedProfiles')
@@ -104,3 +108,60 @@ function parseSupportedProfiles(xmlSupportedProfiles: any, strict: boolean): IMO
104108

105109
return parsed
106110
}
111+
112+
export namespace XMLDefaultActiveX {
113+
export function fromXML(xml: AnyXMLValue, strict: boolean): IMOSDefaultActiveX[] {
114+
const mosTypes = getParseMosTypes(strict)
115+
const handleError = getHandleError('defaultActiveX')
116+
117+
const defaultActiveX: IMOSDefaultActiveX[] = []
118+
for (const xmlObj of ensureArray(xml)) {
119+
if (!xmlObj) continue
120+
121+
if (typeof xmlObj !== 'object') {
122+
if (strict) throw new Error(`defaultActiveX: Expected an object, got: "${xmlObj}"`)
123+
else continue
124+
}
125+
if (typeof xmlObj === 'object' && Array.isArray(xmlObj)) {
126+
if (strict)
127+
throw new Error(`defaultActiveX: Expected an object, got an array: "${JSON.stringify(xmlObj)}"`)
128+
else continue
129+
}
130+
131+
const parsedObj: IMOSDefaultActiveX = {
132+
mode: handleError(
133+
mosTypes.stringEnum.createRequired,
134+
{ enum: IMOSListMachInfoDefaultActiveXMode, value: xmlObj.mode },
135+
'mode'
136+
),
137+
controlFileLocation: handleError(
138+
mosTypes.string.createRequired,
139+
xmlObj.controlFileLocation,
140+
'controlFileLocation'
141+
),
142+
controlSlug: handleError(mosTypes.mosString128.createRequired, xml.controlSlug, 'controlSlug'),
143+
controlName: handleError(mosTypes.string.createRequired, xmlObj.controlName, 'controlName'),
144+
controlDefaultParams: handleError(
145+
mosTypes.string.createRequired,
146+
xmlObj.controlDefaultParams,
147+
'controlDefaultParams'
148+
),
149+
}
150+
151+
defaultActiveX.push(parsedObj)
152+
}
153+
return defaultActiveX
154+
}
155+
export function toXML(xml: XMLBuilder.XMLElement, objs: IMOSDefaultActiveX[] | undefined, strict: boolean): void {
156+
if (objs === undefined) return
157+
for (const obj of objs) {
158+
const xmlItem = addTextElementInternal(xml, 'defaultActiveX', undefined, undefined, strict)
159+
160+
addTextElementInternal(xmlItem, 'mode', obj.mode, undefined, strict)
161+
addTextElementInternal(xmlItem, 'controlFileLocation', obj.controlFileLocation, undefined, strict)
162+
addTextElementInternal(xmlItem, 'controlSlug', obj.controlSlug, undefined, strict)
163+
addTextElementInternal(xmlItem, 'controlName', obj.controlName, undefined, strict)
164+
addTextElementInternal(xmlItem, 'controlDefaultParams', obj.controlDefaultParams, undefined, strict)
165+
}
166+
}
167+
}

0 commit comments

Comments
 (0)