@stordata/vsphere-soapify
Version:
A NodeJS abstraction layer for the vSphere SOAP API
142 lines (108 loc) • 4.02 kB
JavaScript
'use strict';
const _ = require('lodash'),
debug = require('debug')('vsphere-soapify'),
{ data, managed } = require('./sdk');
module.exports = class Parser {
constructor(client) {
this.client = client;
}
parse(object, defaultType = 'xsd:string') {
if (object === undefined || object === null) {
return object;
}
const rawType = this.extractType(object, defaultType),
[, arrayOf,, type] = /^(ArrayOf)?(xsd:)?(.+)/.exec(rawType);
/**
* "ArrayOf" types are handled either:
* - With a dedicated parser if one exists, in this case all parsing is done by the parser itself
* - By parsing every array item using the correct parser (recurses through the `parse` function)
*/
if (arrayOf) {
const parser = `parse${rawType}`,
array = extractArray(object, type);
return parser in this ? this[parser](array) : array.map(item => this.parse(item, type));
}
const Type = `${type.charAt(0).toUpperCase()}${type.substring(1)}`,
parser = `parse${Type}`;
// A dedicated parser exists for this type, use it
if (parser in this) {
return this[parser](object);
}
// We want a complex type, check if it is mapped in the library and use it
if (Type in data) {
return new data[Type](this.client, object);
}
// The complex type is not mapped, check if we have a default type and it is mapped, then use it
if (defaultType in data) {
return new data[defaultType](this.client, object);
}
const str = this.parseString(object); // This will parse all Enumerated types as string
if (!str) {
debug('Parser failed to handle %j with detected type %s and default type %s. Returning the raw object', object, Type, defaultType);
}
return str || object; // Returns the object as-is if we failed
}
extractType(object, defaultType = data.DynamicData) {
const type = typeof defaultType === 'string' ? defaultType : defaultType.name;
if (typeof object !== 'object' || !object.attributes) {
return type;
}
return object.attributes['xsi:type'] || object.attributes['XMLSchema-instance:type'] || type;
}
parseString(val) {
return typeof val === 'string' ? val : val.$value;
}
parseBoolean(val) {
return typeof val === 'boolean' ? val : (val.$value || val) === 'true';
}
parseByte(val) {
return this.parseLong(val);
}
parseShort(val) {
return this.parseLong(val);
}
parseInt(val) {
return this.parseLong(val);
}
parseLong(val) {
return typeof val === 'number' ? val : Number(val.$value || val);
}
parseDateTime(val) {
return val instanceof Date ? val : new Date(val.$value || val);
}
parseArrayOfKeyAnyValue(array) {
return array.reduce((result, kv) => ({ ...result, [kv.key]: this.parse(kv.value) }), {});
}
parseArrayOfOptionValue(array) {
return this.parseArrayOfKeyAnyValue(array);
}
parseManagedObjectReference(val, propSet) {
const { $value, attributes } = val,
type = attributes && 'type' in attributes && attributes.type,
Class = type && type in managed ? managed[type] : managed.ManagedEntity;
return new Class(this.client, $value, propSet);
}
parseObjectContent(val) {
return this.parseManagedObjectReference(val.obj, val.propSet);
}
parseDecimalWorldWideName(val) {
return BigInt(val) // You know, for 64 bits
.toString(16) // Because VMWare sends the WWN in decimal
.toUpperCase() // You know, for matching UCS ones
.replace(/(..)(..)(..)(..)(..)(..)(..)/, '$1:$2:$3:$4:$5:$6:$7:'); // Ultra-advanced, next-gen, uber-powered RegEx wizardry
}
};
//
function extractArray(object, type) {
if (Array.isArray(object)) {
return object;
}
if (type && typeof object === 'object') {
for (const candidate of [type, type.toLowerCase()]) {
if (candidate in object) {
return _.castArray(object[candidate]);
}
}
}
return [];
}