3 * Copyright 2016 RIFT.IO Inc
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
21 // the models to be transformed into the output DSL JSON meta file
22 var yang
= [require('./json-nsd.json'), require('./json-vnfd.json')];
24 var _
= require('lodash');
25 var inet
= require('./ietf-inet-types.yang.json');
28 resolvePath(obj
, path
) {
29 // supports a.b, a[1] and foo[bar], etc.
30 // where obj is ['nope', 'yes', {a: {b: 1}, foo: 2}]
31 // then [1] returns 'yes'; [2].a.b returns 1; [2].a[foo] returns 2;
32 path
= path
.split(/[\.\[\]]/).filter(d
=> d
);
33 return path
.reduce((r
, p
) => {
39 assignPathValue(obj
, path
, value
) {
40 path
= path
.split(/[\.\[\]]/).filter(d
=> d
);
41 // enable look-ahead to determine if type is array or object
42 const pathCopy
= path
.slice();
43 // last item in path used to assign value on the resolved object
44 const name
= path
.pop();
45 const resolvedObj
= path
.reduce((r
, p
, i
) => {
46 if (typeof(r
[p
]) !== 'object') {
47 // look-ahead to see if next path item is a number
48 const isArray
= !isNaN(parseInt(pathCopy
[i
+ 1], 10));
49 r
[p
] = isArray
? [] : {}
53 resolvedObj
[name
] = value
;
57 var isType
= d
=> /^(leaf|leaf-list|list|container|choice|case|uses)$/.test(d
);
59 function deriveCardinalityFromProperty(property
, typeName
) {
60 if (String(property
.mandatory
) === 'true') {
63 let min
= 0, max
= Infinity
;
64 if (property
.hasOwnProperty('min-elements')) {
65 min
= parseInt(property
['min-elements'], 10) || 0;
67 if (property
.hasOwnProperty('max-elements')) {
68 max
= parseInt(property
['max-elements'], 10) || Infinity
;
70 if (!/^(list|leaf-list)$/.test(typeName
)) {
80 return String(min
) + '..' + (max
=== Infinity
? 'N' : max
);
83 function cleanWhitespace(text
) {
84 if (typeof text
=== 'string') {
85 return text
.replace(/\s+/g, ' ');
90 function buildProperties(typeData
, typeName
) {
92 Object
.keys(typeData
).forEach(name
=> {
93 var property
= typeData
[name
];
94 var listKey
= typeName
=== 'list' ? String(property
.key
).split(/\s/).filter(k
=> k
&& k
!== 'undefined') : false;
98 description
: cleanWhitespace(property
.description
),
99 cardinality
: deriveCardinalityFromProperty(property
, typeName
),
100 'data-type': property
.type
,
101 properties
: Object
.keys(property
).filter(isType
).reduce((r
, childType
) => {
102 return r
.concat(buildProperties(property
[childType
], childType
));
108 properties
.push(meta
);
113 function lookupUses(uses
, yang
) {
114 function doLookup(lookupTypeName
) {
116 // warn: hardcoded prefix support for mano-types - other prefixes will be ignored
117 if (/^manotypes:/.test(lookupTypeName
)) {
118 var moduleName
= lookupTypeName
.split(':')[1];
119 key
= ['dependencies.mano-types.module.mano-types.grouping', moduleName
].join('.');
121 var name
= yang
.name
.replace(/^rw-/, '');
122 key
= ['dependencies', name
, 'module', name
, 'grouping', lookupTypeName
].join('.');
124 return utils
.resolvePath(yang
, key
);
126 if (typeof uses
=== 'object') {
127 return Object
.keys(uses
).reduce((result
, key
) => {
128 var found
= doLookup(key
);
129 Object
.keys(found
).filter(isType
).forEach(type
=> {
130 var property
= result
[type
] || (result
[type
] = {});
131 Object
.assign(property
, found
[type
]);
135 } else if (typeof uses
=== 'string') {
136 return doLookup(uses
);
141 function lookupTypedef(property
, yang
) {
143 var lookupTypeName
= property
.type
;
144 // warn: hardcoded prefix support - other prefixes will be ignored
145 if (/^manotypes:/.test(lookupTypeName
)) {
146 var lookupName
= lookupTypeName
.split(':')[1];
147 key
= ['dependencies.mano-types.module.mano-types.typedef', lookupName
].join('.');
148 } else if (/^inet:/.test(lookupTypeName
)) {
149 var lookupName
= lookupTypeName
.split(':')[1];
151 key
= ['schema.module.ietf-inet-types.typedef', lookupName
].join('.');
154 return utils
.resolvePath(yang
, key
);
158 function resolveUses(property
, yang
) {
159 var childData
= property
.uses
;
160 var resolved
= lookupUses(childData
, yang
);
161 //console.log('uses', childData, 'found', resolved);
162 Object
.keys(resolved
).forEach(type
=> {
163 var parentTypes
= property
[type
] || (property
[type
] = {});
164 // copy types into the parent types bucket
165 Object
.assign(parentTypes
, resolveReferences(yang
, resolved
[type
]));
167 delete property
.uses
;
170 function resolveTypedef(property
, yang
) {
171 if (/:/.test(property
.type
)) {
172 var found
= lookupTypedef(property
, yang
);
174 Object
.assign(property
, found
);
179 function resolveReferences(yang
, data
) {
180 var dataClone
= _
.cloneDeep(data
);
181 function doResolve(typeData
) {
182 Object
.keys(typeData
).forEach(name
=> {
183 var property
= typeData
[name
];
184 resolveTypedef(property
, yang
);
185 Object
.keys(property
).filter(isType
).forEach(childType
=> {
186 if (childType
=== 'uses') {
187 resolveUses(property
, yang
);
189 doResolve(property
[childType
]);
194 doResolve(dataClone
);
198 function module(yang
) {
200 var name
= yang
.name
.replace(/^rw-/, '');
202 throw 'no name given in json yang';
204 const path
= ['container', name
+ '-catalog'].join('.');
205 module
= utils
.resolvePath(yang
, path
);
208 module
= utils
.resolvePath(yang
, ['schema', 'module', name
, path
].join('.'));
211 module
= utils
.resolvePath(yang
, ['dependencies', name
, 'module', name
, path
].join('.'));
214 throw 'cannot find the module' + name
;
217 // module/agument/nsd:nsd-catalog/nsd:nsd/meta
218 const augLeafPath
= ['schema.module', 'rw-' + name
, 'augment', '/' + name
+ ':' + name
+ '-catalog/' + name
+ ':' + name
, 'leaf'];
219 const meta
= utils
.resolvePath(yang
, augLeafPath
.concat('meta').join('.'));
221 const putLeafPath
= ['dependencies', name
, 'module', name
, path
, 'list', name
, 'leaf'];
224 utils
.assignPathValue(yang
, putLeafPath
.concat(['meta']).join('.'), meta
);
227 // module/agument/nsd:nsd-catalog/nsd:nsd/logo
228 const logo
= utils
.resolvePath(yang
, augLeafPath
.concat('logo').join('.'));
230 utils
.assignPathValue(yang
, putLeafPath
.concat(['logo']).join('.'), logo
);
232 var data
= module
.list
;
234 return {name
: name
, data
: resolveReferences(yang
, data
)};
238 function reduceModule(result
, module
) {
239 result
[module
.name
] = buildProperties(module
.data
, 'list')[0];
243 var result
= yang
.map(module
).reduce(reduceModule
, {});
245 console
.log(JSON
.stringify(result
, null, 5));