helpers = require('../helpers'); AWS = helpers.AWS
describe 'AWS.XML.Parser', ->
parse = (xml, rules, callback) ->
if rules
shape = AWS.Model.Shape.create(rules, api: {})
else
shape = {}
callback.call(this, new AWS.XML.Parser().parse(xml, shape))
describe 'default behavior', ->
rules = null # no rules, rely on default parsing behavior
it 'returns empty object when string is empty', ->
parse '', null, (data) -> expect(data).toEqual({})
it 'returns an empty object from an empty document', ->
xml = ''
parse xml, rules, (data) ->
expect(data).toEqual({})
it 'returns empty elements as empty string', ->
xml = ''
parse xml, rules, (data) ->
expect(data).toEqual({element:''})
it 'converts string elements to properties', ->
xml = 'abcxyz'
parse xml, rules, (data) ->
expect(data).toEqual({foo:'abc', bar:'xyz'})
it 'converts nested elements into objects', ->
xml = 'yuck'
parse xml, rules, (data) ->
expect(data).toEqual({foo:{bar:'yuck'}})
it 'returns everything as a string (even numbers)', ->
xml = '123'
parse xml, rules, (data) ->
expect(data).toEqual({count:'123'})
it 'ignores xmlns on the root element', ->
xml = 'xyz'
parse xml, rules, (data) ->
expect(data).toEqual({Abc:'xyz'})
describe 'structures', ->
it 'returns empty objects as {}', ->
xml = ' '
rules =
type: 'structure'
members:
Item:
type: 'structure'
members:
Name:
type: 'string'
parse xml, rules, (data) ->
expect(data).toEqual({Item:{}})
it 'parses attributes from tags', ->
xml = '
'
rules =
type: 'structure'
members:
Item:
type: 'structure'
members:
Name:
type: 'string'
xmlAttribute: true
locationName: 'xsi:name'
parse xml, rules, (data) ->
expect(data).toEqual({Item:{Name: 'name'}})
describe 'lists', ->
it 'returns empty lists as []', ->
xml = ''
rules =
type: 'structure'
members:
items:
type: 'list'
member:
type: 'string'
parse xml, rules, (data) ->
expect(data).toEqual({items:[]})
it 'returns missing lists as []', ->
xml = ''
rules =
type: 'structure'
members:
items:
type: 'list'
member:
type: 'string'
parse xml, rules, (data) ->
expect(data).toEqual({items:[]})
it 'Converts xml lists of strings into arrays of strings', ->
xml = """
abc
xyz
"""
rules =
type: 'structure'
members:
items:
type: 'list'
member: {}
parse xml, rules, (data) ->
expect(data).toEqual({items:['abc','xyz']})
it 'observes list member names when present', ->
xml = """
- abc
- xyz
"""
rules =
type: 'structure'
members:
items:
type: 'list'
member:
locationName: 'item'
parse xml, rules, (data) ->
expect(data).toEqual({items:['abc','xyz']})
it 'can parse lists of strucures', ->
xml = """
abc>
xyz>
"""
rules =
type: 'structure'
members:
People:
type: 'list'
member:
type: 'structure'
members:
Name:
type: 'string'
parse xml, rules, (data) ->
expect(data).toEqual({People:[{Name:'abc'},{Name:'xyz'}]})
it 'can parse lists of strucures with renames', ->
xml = """
abc>
xyz>
"""
rules =
type: 'structure'
members:
People:
type: 'list'
member:
type: 'structure'
locationName: 'Person'
members:
Name:
type: 'string'
parse xml, rules, (data) ->
expect(data).toEqual({People:[{Name:'abc'},{Name:'xyz'}]})
describe 'flattened lists', ->
xml = """
Unknown
John Doe
Jane Doe
"""
it 'collects sibling elements of the same name', ->
rules =
type: 'structure'
members:
person:
type: 'structure'
members:
name: {}
aka:
type: 'list'
flattened: true
member:
locationName: 'alias'
parse xml, rules, (data) ->
expect(data).toEqual({person:{name:'Unknown',aka:['John Doe', 'Jane Doe']}})
it 'flattened lists can be composed of complex obects', ->
xml = """
Name
1
2
3
4
"""
rules =
type: 'structure'
members:
name:
type: 'string'
values:
type: 'list'
flattened: true
member:
locationName: 'complexValue'
type: 'structure'
members:
a: type: 'integer'
b: type: 'integer'
values = {name:'Name',values:[{a:1,b:2},{a:3,b:4}]}
parse xml, rules, (data) ->
expect(data).toEqual(values)
it 'can parse flattened lists of complex objects', ->
xml = """
2
abc
xyz
"""
rules =
type: 'structure'
members:
Count:
type: 'integer'
People:
type: 'list'
flattened: true
member:
type: 'structure'
locationName: 'Person'
members:
Name: {}
parse xml, rules, (data) ->
expect(data).toEqual({Count:2,People:[{Name:'abc'},{Name:'xyz'}]})
describe 'maps', ->
describe 'non-flattened', ->
it 'expects entry, key, and value elements by default', ->
# example from IAM GetAccountSummary (output)
xml = """
Groups
31
GroupsQuota
50
UsersQuota
150
"""
rules =
type: 'structure'
members:
SummaryMap:
type: 'map'
value:
type: 'integer'
parse xml, rules, (data) ->
expect(data).toEqual(SummaryMap:{Groups:31,GroupsQuota:50,UsersQuota:150})
it 'can use alternate names for key and value elements', ->
# using Property/Count instead of key/value, also applied a name
# trait to the Summary map to rename it
xml = """
Groups
31
GroupsQuota
50
UsersQuota
150
"""
rules =
type: 'structure'
members:
Summary:
type: 'map'
locationName: 'SummaryMap',
key:
locationName: 'Property'
value:
type: 'integer'
locationName: 'Count'
parse xml, rules, (data) ->
expect(data).toEqual(Summary:{Groups:31,GroupsQuota:50,UsersQuota:150})
describe 'flattened', ->
it 'expects key and value elements by default', ->
xml = """
color
red
size
large
"""
rules =
type: 'structure'
members:
Attributes:
type: 'map'
flattened: true
parse xml, rules, (data) ->
expect(data).toEqual({Attributes:{color:'red',size:'large'}})
it 'can use alternate names for key and value elements', ->
# using AttrName/AttrValue instead of key/value, also applied a name
# trait to the Attributes map
xml = """
age
35
height
72
"""
rules =
type: 'structure'
members:
Attributes:
locationName: 'Attribute'
type: 'map'
flattened: true
key:
locationName: 'AttrName'
value:
locationName: 'AttrValue'
type: 'integer'
parse xml, rules, (data) ->
expect(data).toEqual({Attributes:{age:35,height:72}})
describe 'booleans', ->
rules =
type: 'structure'
members:
enabled:
type: 'boolean'
it 'converts the string "true" in to the boolean value true', ->
xml = "true"
parse xml, rules, (data) ->
expect(data).toEqual({enabled:true})
it 'converts the string "false" in to the boolean value false', ->
xml = "false"
parse xml, rules, (data) ->
expect(data).toEqual({enabled:false})
it 'converts the empty elements into null', ->
xml = ""
parse xml, rules, (data) ->
expect(data).toEqual({enabled:null})
describe 'timestamp', ->
rules =
type: 'structure'
members:
CreatedAt:
type: 'timestamp'
it 'returns an empty element as null', ->
xml = ""
parse xml, rules, (data) ->
expect(data).toEqual({CreatedAt:null})
it 'understands unix timestamps', ->
timestamp = 1349908100
date = new Date(timestamp * 1000)
xml = "#{timestamp}"
parse xml, rules, (data) ->
expect(data).toEqual({CreatedAt:date})
it 'understands basic iso8601 strings', ->
timestamp = '2012-10-10T15:47:10.001Z'
date = new Date(timestamp)
xml = "#{timestamp}"
parse xml, rules, (data) ->
expect(data).toEqual({CreatedAt:date})
it 'understands basic rfc822 strings', ->
timestamp = 'Wed, 10 Oct 2012 15:59:55 UTC'
date = new Date(timestamp)
xml = "#{timestamp}"
parse xml, rules, (data) ->
expect(data).toEqual({CreatedAt:date})
it 'throws an error when unable to determine the format', ->
timestamp = 'bad-date-format'
xml = "#{timestamp}"
message = 'unhandled timestamp format: ' + timestamp
error = {}
try
parse(xml, rules, ->)
catch e
error = e
expect(error.message).toEqual(message)
describe 'numbers', ->
rules =
type: 'structure'
members:
decimal:
type: 'float'
it 'float parses elements types as integer', ->
xml = "123.456"
parse xml, rules, (data) ->
expect(data).toEqual({decimal:123.456})
it 'returns null for empty elements', ->
xml = ""
parse xml, rules, (data) ->
expect(data).toEqual({decimal:null})
describe 'integers', ->
rules =
type: 'structure'
members:
count:
type: 'integer'
it 'integer parses elements types as integer', ->
xml = "123"
parse xml, rules, (data) ->
expect(data).toEqual({count:123})
it 'returns null for empty elements', ->
xml = ""
parse xml, rules, (data) ->
expect(data).toEqual({count:null})
describe 'renaming elements', ->
it 'can rename scalar elements', ->
rules =
type: 'structure'
members:
aka:
locationName: 'alias'
xml = "John Doe"
parse xml, rules, (data) ->
expect(data).toEqual({aka:'John Doe'})
it 'can rename nested elements', ->
rules =
type: 'structure'
members:
person:
members:
name: {}
aka:
locationName: 'alias'
xml = "JoeJohn Doe"
parse xml, rules, (data) ->
expect(data).toEqual({person:{name:'Joe',aka:'John Doe'}})
describe 'base64 encoded strings', ->
it 'base64 decodes string elements with encoding="base64"', ->
rules =
type: 'structure'
members:
Value:
type: 'string'
xml = """
Zm9v
"""
parse xml, rules, (data) ->
expect(data.Value.toString()).toEqual('foo')
rules =
type: 'structure'
members:
Value: {}
xml = """
Zm9v
"""
parse xml, rules, (data) ->
expect(data.Value.toString()).toEqual('foo')
describe 'elements with XML namespaces', ->
it 'strips the xmlns element', ->
rules =
type: 'structure'
members:
List:
type: 'list'
member:
type: 'structure'
members:
Attr1: {}
Attr2:
type: 'structure'
members:
Foo: {}
xml = """
abc
bar
"""
parse xml, rules, (data) ->
expect(data).toEqual({List:[{Attr1:'abc',Attr2:{Foo:'bar'}}]})
describe 'parsing errors', ->
it 'throws an error when unable to parse the xml', ->
xml = 'asdf'
rules = {}
error = {}
try
new AWS.XML.Parser().parse(xml, rules)
catch e
error = e
expect(error.code).toEqual('XMLParserError')