-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
153 lines (137 loc) · 3.79 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
const superagent = require('superagent')
const xmlReader = require('xml-reader')
const ADDS_BASE_URI = 'https://www.aviationweather.gov'
const ADDS_API_URI = `${ADDS_BASE_URI}/adds/dataserver_current/httpparam`
const DATA_SOURCES = [
'metars',
'tafs',
'aircraftreports',
'airsigmets',
'gairmets',
'stations'
]
const productTag = dataSource => {
return {
tafs: 'TAF',
metars: 'METAR',
airsigmets: 'AIRSIGMET',
aircraftreports: 'AircraftReport',
gairmets: 'GAIRMET',
stations: 'Station'
}[dataSource.toLowerCase()]
}
const tryParseNumber = str => {
const number = Number(str)
const float = parseFloat(number)
if (float === number) {
return float
}
const int = parseInt(number)
if (int === number) {
return int
}
return str
}
const extractAttributes = field => {
if (!field.attributes) {
return
}
const keys = Object.keys(field.attributes)
const attributes = {}
keys.map(k => {
attributes[k] = tryParseNumber(field.attributes[k])
})
return attributes
}
const extractSingleValue = field => {
if (Object.keys(field.attributes).length > 0) {
return {
value: tryParseNumber(field.children[0].value),
attributes: extractAttributes(field)
}
} else {
return tryParseNumber(field.children[0].value)
}
}
const extractData = element => {
if (!element.children || element.children.length === 0) {
return null
}
const data = { ...extractAttributes(element) }
element.children.map(field => {
// If the element has no value, just grab any attributes
if (field.children.length === 0) {
if (data[field.name] !== undefined) {
if (data[field.name].length > 0) {
data[field.name].push(extractAttributes(field))
} else {
data[field.name] = [ data[field.name], extractAttributes(field) ]
}
} else {
data[field.name] = extractAttributes(field)
}
return
}
// If the element has a value, extract it
if (field.children.length === 1) {
data[field.name] = extractSingleValue(field)
return
}
// If element has nested child elements, resursively extract values
if (field.children.length > 1) {
if (data[field.name] !== undefined) {
if (data[field.name].length > 0) {
data[field.name].push(extractData(field))
} else {
data[field.name] = [ data[field.name], extractData(field) ]
}
} else {
data[field.name] = extractData(field)
}
}
})
return data
}
const fetch = async (options) => {
try {
const response = await superagent.get(ADDS_API_URI)
.query({
requestType: 'retrieve',
format: 'xml',
...options
})
if (!response || !response.ok || !response.text || response.text.match(/<error>/)) {
throw new Error('Error fetching from ADDS', {
statusCode: response.statusCode,
text: response.text
})
}
const product = productTag(options.dataSource)
const reader = xmlReader.create({ stream: true })
const results = []
reader.on(`tag:${product}`, element => {
results.push(extractData(element))
})
await reader.parse(response.text)
return results
} catch (error) {
console.error(error)
}
}
const ADDS = async (...args) => {
if (!args) {
throw new Error('No arguments provided to ADDS()')
}
if (args.length === 2 && typeof args[0] === 'string' && typeof args[1] === 'object') {
const dataSource = args[0]
const options = args[1]
if (DATA_SOURCES.includes(dataSource)) {
return fetch({ dataSource, ...options })
}
}
throw new Error('Invalid arguments provided to ADDS()')
}
module.exports = ADDS
module.exports.ADDS_BASE_URI = ADDS_BASE_URI
module.exports.ADDS_API_URI = ADDS_API_URI
module.exports.DATA_SOURCES = DATA_SOURCES