2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
20 package org.onap.aai.util.genxsd;
22 import java.io.BufferedWriter;
24 import java.io.FileNotFoundException;
25 import java.io.IOException;
26 import java.nio.charset.Charset;
27 import java.nio.file.Files;
28 import java.nio.file.Path;
29 import java.nio.file.Paths;
30 import java.util.HashMap;
31 import java.util.LinkedHashSet;
34 import java.util.SortedSet;
35 import java.util.StringTokenizer;
36 import java.util.TreeMap;
37 import java.util.TreeSet;
38 import java.util.Vector;
40 import javax.xml.parsers.ParserConfigurationException;
42 import org.apache.commons.lang3.StringUtils;
43 import org.onap.aai.edges.EdgeIngestor;
44 import org.onap.aai.edges.EdgeRule;
45 import org.onap.aai.edges.EdgeRuleQuery;
46 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
47 import org.onap.aai.exceptions.AAIException;
48 import org.onap.aai.setup.SchemaVersion;
49 import org.onap.aai.setup.SchemaVersions;
50 import org.onap.aai.nodes.NodeIngestor;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53 import org.w3c.dom.Element;
54 import org.w3c.dom.NodeList;
55 import org.xml.sax.SAXException;
57 import com.google.common.collect.Multimap;
59 public class NodesYAMLfromOXM extends OxmFileProcessor {
60 private static final Logger logger = LoggerFactory.getLogger("GenerateXsd.class");
61 private static final String root = "../aai-schema/src/main/resources";
62 private static final String autoGenRoot = "aai-schema/src/main/resources";
63 private static final String generateTypeYAML = "yaml";
64 private static final String normalStartDir = "aai-core";
65 private static final String yaml_dir = (((System.getProperty("user.dir") != null) && (!System.getProperty("user.dir").contains(normalStartDir))) ? autoGenRoot : root) + "/aai_swagger_yaml";
66 private StringBuilder inventoryDefSb = null;
67 private Map<String,String> operationDefinitions = new HashMap<>();
69 private String basePath;
71 public NodesYAMLfromOXM(String basePath, SchemaVersions schemaVersions, NodeIngestor ni, EdgeIngestor ei){
72 super(schemaVersions, ni,ei);
73 this.basePath = basePath;
75 public void setOxmVersion(File oxmFile, SchemaVersion v) {
76 super.setOxmVersion(oxmFile, v);
78 public void setXmlVersion(String xml, SchemaVersion v){
79 super.setXmlVersion(xml, v);
82 public void setVersion(SchemaVersion v) {
87 public String getDocumentHeader() {
88 StringBuffer sb = new StringBuffer();
89 sb.append("swagger: \"2.0\"\ninfo:\n ");
90 sb.append("description: |");
91 if ( versionSupportsSwaggerDiff(v.toString())) {
92 sb.append("\n\n [Differences versus the previous schema version]("+"apidocs/aai_swagger_" + v.toString() + ".diff)");
94 sb.append("\n\n Copyright © 2017-18 AT&T Intellectual Property. All rights reserved.\n\n Licensed under the Creative Commons License, Attribution 4.0 Intl. (the "License"); you may not use this documentation except in compliance with the License.\n\n You may obtain a copy of the License at\n\n (https://creativecommons.org/licenses/by/4.0/)\n\n Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.\n\n This document is best viewed with Firefox or Chrome. Nodes can be found by appending /#/definitions/node-type-to-find to the path to this document. Edge definitions can be found with the node definitions.\n version: \"" + v.toString() +"\"\n");
95 sb.append(" title: Active and Available Inventory REST API\n");
96 sb.append(" license:\n name: Apache 2.0\n url: http://www.apache.org/licenses/LICENSE-2.0.html\n");
97 sb.append(" contact:\n name:\n url:\n email:\n");
98 sb.append("host:\nbasePath: " + basePath + "/" + v.toString() + "\n");
99 sb.append("schemes:\n - https\npaths:\n");
100 return sb.toString();
103 protected void init() throws ParserConfigurationException, SAXException, IOException, AAIException, FileNotFoundException, EdgeRuleNotFoundException {
108 public String process() throws ParserConfigurationException, SAXException, IOException, AAIException, FileNotFoundException, EdgeRuleNotFoundException {
109 StringBuffer sb = new StringBuffer();
110 StringBuffer pathSb = new StringBuffer();
111 NodeGetOperation.resetContainers();
114 } catch(Exception e) {
115 logger.error( "Error initializing " + this.getClass());
118 pathSb.append(getDocumentHeader());
119 StringBuffer definitionsSb = new StringBuffer();
122 for ( int i = 0; i < javaTypeNodes.getLength(); ++ i ) {
123 elem = (Element)javaTypeNodes.item(i);
124 javaTypeName = elem.getAttribute("name");
125 if ( !"Inventory".equals(javaTypeName ) ) {
126 if ( generatedJavaType.containsKey(javaTypeName) ) {
129 // will combine all matching java-types
130 elem = getJavaTypeElementSwagger(javaTypeName );
133 XSDElement javaTypeElement = new XSDElement(elem);
135 logger.debug("External: "+javaTypeElement.getAttribute("name")+"/"+getXmlRootElementName(javaTypeName));
136 if ( javaTypeName == null ) {
137 String msg = "Invalid OXM file: <java-type> has no name attribute in " + oxmFile;
139 throw new AAIException(msg);
141 namespaceFilter.add(getXmlRootElementName(javaTypeName));
142 processJavaTypeElementSwagger( javaTypeName, javaTypeElement, pathSb,
143 definitionsSb, null, null, null, null, null, null);
146 // sb.append(getDocumentHeader());
147 // sb.append(totalPathSbAccumulator);
148 sb.append(appendOperations());
149 sb.append(appendDefinitions());
150 PutRelationPathSet prp = new PutRelationPathSet(v);
151 prp.generateRelations(ei);
152 return sb.toString();
155 public String appendDefinitions() {
156 return appendDefinitions(null);
159 public String appendDefinitions(Set<String> namespaceFilter) {
160 if ( inventoryDefSb != null ) {
161 javaTypeDefinitions.put("inventory", inventoryDefSb.toString());
163 StringBuffer sb = new StringBuffer("definitions:\n");
164 Map<String, String> sortedJavaTypeDefinitions = new TreeMap<>(javaTypeDefinitions);
166 for (Map.Entry<String, String> entry : sortedJavaTypeDefinitions.entrySet()) {
167 if(namespaceFilter != null && (! namespaceFilter.contains(entry.getKey()))) {
170 sb.append(entry.getValue());
172 return sb.toString();
175 private String processJavaTypeElementSwagger( String javaTypeName, Element javaTypeElement,
176 StringBuffer pathSb, StringBuffer definitionsSb, String path, String tag, String opId,
177 String getItemName, StringBuffer pathParams, String validEdges) {
179 String xmlRootElementName = getXMLRootElementName(javaTypeElement);
180 StringBuilder definitionsLocalSb = new StringBuilder(256);
182 String useTag = null;
183 String useOpId = null;
184 logger.debug("tag="+tag);
186 if(tag != null && (! validTag(tag))) {
187 logger.debug("tag="+tag+"; javaTypeName="+javaTypeName);
190 if ( !javaTypeName.equals("Inventory") ) {
191 if ( javaTypeName.equals("AaiInternal"))
194 useOpId = javaTypeName;
196 useOpId = opId + javaTypeName;
198 useTag = javaTypeName;
201 path = xmlRootElementName.equals("inventory") ? "" : (path == null) ? "/" + xmlRootElementName : path + "/" + xmlRootElementName;
202 XSDJavaType javaType = new XSDJavaType(javaTypeElement);
203 if ( getItemName != null) {
204 if ( getItemName.equals("array") )
205 return javaType.getArrayType();
207 return javaType.getItemName();
210 NodeList parentNodes = javaTypeElement.getElementsByTagName("java-attributes");
211 if ( parentNodes.getLength() == 0 ) {
212 logger.debug( "no java-attributes for java-type " + javaTypeName);
216 String pathDescriptionProperty = javaType.getPathDescriptionProperty();
217 String container = javaType.getContainerProperty();
218 Vector<String> indexedProps = javaType.getIndexedProps();
219 Vector<String> containerProps = new Vector<String>();
220 if(container != null) {
221 logger.debug("javaTypeName " + javaTypeName + " container:" + container +" indexedProps:"+indexedProps);
224 Element parentElement = (Element)parentNodes.item(0);
225 NodeList xmlElementNodes = parentElement.getElementsByTagName("xml-element");
227 StringBuffer sbParameters = new StringBuffer();
228 StringBuffer sbRequired = new StringBuffer();
231 StringBuffer sbProperties = new StringBuffer();
233 if ( appliedPaths.containsKey(path))
236 StringTokenizer st = new StringTokenizer(path, "/");
237 logger.debug("path: " + path + " st? " + st.toString());
238 if ( st.countTokens() > 1 && getItemName == null ) {
239 logger.debug("appliedPaths: " + appliedPaths + " containsKey? " + appliedPaths.containsKey(path));
240 appliedPaths.put(path, xmlRootElementName);
242 Vector<String> addTypeV = null;
243 for ( int i = 0; i < xmlElementNodes.getLength(); ++i ) {
244 XSDElement xmlElementElement = new XSDElement((Element)xmlElementNodes.item(i));
245 if ( !xmlElementElement.getParentNode().isSameNode(parentElement))
247 String elementDescription=xmlElementElement.getPathDescriptionProperty();
248 if(getItemName == null) {
249 addTypeV = xmlElementElement.getAddTypes(v.toString());
251 if ( "true".equals(xmlElementElement.getAttribute("xml-key"))) {
252 path += "/{" + xmlElementElement.getAttribute("name") + "}";
254 logger.debug("path: " + path);
255 logger.debug( "xmlElementElement.getAttribute(required):"+xmlElementElement.getAttribute("required") );
257 if ( ("true").equals(xmlElementElement.getAttribute("required"))) {
258 if ( requiredCnt == 0 )
259 sbRequired.append(" required:\n");
261 if ( addTypeV == null || addTypeV.isEmpty()) {
262 sbRequired.append(" - " + xmlElementElement.getAttribute("name") + "\n");
264 for ( int k = 0; k < addTypeV.size(); ++k ) {
265 sbRequired.append(" - " + getXmlRootElementName(addTypeV.elementAt(k)) + ":\n");
270 if ( "true".equals(xmlElementElement.getAttribute("xml-key")) ) {
271 sbParameters.append(xmlElementElement.getPathParamYAML(elementDescription));
273 if ( indexedProps != null
274 && indexedProps.contains(xmlElementElement.getAttribute("name") ) ) {
275 containerProps.add(xmlElementElement.getQueryParamYAML(elementDescription));
276 NodeGetOperation.addContainerProps(container, containerProps);
277 } else if ( indexedProps == null || indexedProps.isEmpty() && "true".equals(xmlElementElement.getAttribute("required")) ){
278 // no indexedProps and element is required, also considered using xml-key in this case
279 containerProps.add(xmlElementElement.getPathParamYAMLRqd(elementDescription, false));
280 NodeGetOperation.addContainerProps(container, containerProps);
282 if ( xmlElementElement.isStandardType()) {
283 sbProperties.append(xmlElementElement.getTypePropertyYAML());
287 // StringBuffer newPathParams = new StringBuffer((pathParams == null ? "" : pathParams.toString())+sbParameters.toString()); //cp8128 don't append the pathParams to sbParameters so that child nodes don't contain the parameters from parent
288 StringBuffer newPathParams = new StringBuffer(sbParameters.toString());
289 for ( int k = 0; addTypeV != null && k < addTypeV.size(); ++k ) {
290 String addType = addTypeV.elementAt(k);
291 namespaceFilter.add(getXmlRootElementName(addType));
292 if ( opId == null || !opId.contains(addType)) {
293 processJavaTypeElementSwagger( addType, getJavaTypeElementSwagger(addType),
294 pathSb, definitionsSb, path, tag == null ? useTag : tag, useOpId, null,
295 newPathParams, validEdges);
297 // need item name of array
298 String itemName = processJavaTypeElementSwagger( addType, getJavaTypeElementSwagger(addType),
299 pathSb, definitionsSb, path, tag == null ? useTag : tag, useOpId,
300 "array", null, null);
302 if ( itemName != null ) {
303 if ( addType.equals("AaiInternal") ) {
304 logger.debug( "addType AaiInternal, skip properties");
306 } else if ( getItemName == null) {
308 sbProperties.append(" " + getXmlRootElementName(addType) + ":\n");
309 sbProperties.append(" type: array\n items:\n");
310 sbProperties.append(" $ref: \"#/definitions/" + (itemName == "" ? "aai-internal" : itemName) + "\"\n");
311 if ( StringUtils.isNotEmpty(elementDescription) )
312 sbProperties.append(" description: " + elementDescription + "\n");
315 if ( ("java.util.ArrayList").equals(xmlElementElement.getAttribute("container-type"))) {
316 // need properties for getXmlRootElementName(addType)
317 namespaceFilter.add(getXmlRootElementName(addType));
318 if(getXmlRootElementName(addType).equals("service-capabilities"))
320 logger.info("arrays: "+ getXmlRootElementName(addType));
322 // newPathParams = new StringBuffer((pathParams == null ? "" : pathParams.toString())+sbParameters.toString()); //cp8128 - change this to not append pathParameters. Just use sbParameters
323 newPathParams = new StringBuffer(sbParameters.toString());
324 processJavaTypeElementSwagger( addType, getJavaTypeElementSwagger(addType),
325 pathSb, definitionsSb, path, tag == null ? useTag : tag, useOpId,
326 null, newPathParams, validEdges);
327 sbProperties.append(" " + getXmlRootElementName(addType) + ":\n");
328 sbProperties.append(" type: array\n items: \n");
329 sbProperties.append(" $ref: \"#/definitions/" + getXmlRootElementName(addType) + "\"\n");
330 if ( StringUtils.isNotEmpty(elementDescription) )
331 sbProperties.append(" description: " + elementDescription + "\n");
334 //Make sure certain types added to the filter don't appear
335 if (nodeFilter.contains(getXmlRootElementName(addType))) {
338 sbProperties.append(" " + getXmlRootElementName(addType) + ":\n");
339 sbProperties.append(" type: object\n");
340 sbProperties.append(" $ref: \"#/definitions/" + getXmlRootElementName(addType) + "\"\n");
343 if ( StringUtils.isNotEmpty(elementDescription) )
344 sbProperties.append(" description: " + elementDescription + "\n");
350 if ( sbParameters.toString().length() > 0 ) {
351 if ( pathParams == null )
352 pathParams = new StringBuffer();
353 pathParams.append(sbParameters);
355 if (indexedProps.isEmpty() && containerProps.isEmpty()){
356 NodeGetOperation get = new NodeGetOperation(useOpId, xmlRootElementName, tag, path, null);
357 String operation = get.toString();
358 if(StringUtils.isNotEmpty(operation)) {
359 operationDefinitions.put(xmlRootElementName, operation);
362 NodeGetOperation get = new NodeGetOperation(useOpId, xmlRootElementName, tag, path, pathParams == null ? "" : pathParams.toString());
363 String operation = get.toString();
364 if(StringUtils.isNotEmpty(operation)) {
365 operationDefinitions.put(xmlRootElementName, operation);
368 logger.debug("opId vs useOpId:"+opId+" vs "+useOpId+" PathParams="+pathParams);
370 if ( generatedJavaType.containsKey(xmlRootElementName) ) {
371 logger.debug("xmlRootElementName(1)="+xmlRootElementName);
374 boolean processingInventoryDef = false;
375 if ( xmlRootElementName.equals("inventory")) {
376 // inventory properties for each oxm to be concatenated
377 processingInventoryDef = true;
378 if ( inventoryDefSb == null ) {
379 inventoryDefSb = new StringBuilder();
380 definitionsSb.append(" " + xmlRootElementName + ":\n");
381 definitionsLocalSb.append(" " + xmlRootElementName + ":\n");
382 definitionsLocalSb.append(" properties:\n");
386 definitionsSb.append(" " + xmlRootElementName + ":\n");
387 definitionsLocalSb.append(" " + xmlRootElementName + ":\n");
389 DeleteFootnoteSet footnotes = new DeleteFootnoteSet(xmlRootElementName);
390 StringBuffer sbEdge = new StringBuffer();
391 LinkedHashSet<String> preventDelete = new LinkedHashSet<String>();
393 String nodeCaption = new String(" ###### Related Nodes\n");
395 EdgeRuleQuery q = new EdgeRuleQuery.Builder(xmlRootElementName).version(v).fromOnly().build();
396 Multimap<String, EdgeRule> results = ei.getRules(q);
397 SortedSet<String> ss=new TreeSet<>(results.keySet());
398 sbEdge.append(nodeCaption);
400 for(String key : ss) {
401 results.get(key).stream().filter((i) -> (i.getFrom().equals(xmlRootElementName) && (! i.isPrivateEdge()))).forEach((i) ->{ logger.info(new String(new StringBuffer(" - TO ").append(i.getTo()).append(i.getDirection().toString()).append(i.getContains())));} );
402 results.get(key).stream().filter((i) -> (i.getFrom().equals(xmlRootElementName) && (! i.isPrivateEdge()))).forEach((i) ->{ sbEdge.append(" - TO "+i.getTo()); EdgeDescription ed = new EdgeDescription(i); String footnote = ed.getAlsoDeleteFootnote(xmlRootElementName); sbEdge.append(ed.getRelationshipDescription("TO", xmlRootElementName)+footnote+"\n"); if(StringUtils.isNotEmpty(footnote)) footnotes.add(footnote);} );
403 results.get(key).stream().filter((i) -> (i.getFrom().equals(xmlRootElementName) && (! i.isPrivateEdge() && i.getPreventDelete().equals("OUT")))).forEach((i) ->{ preventDelete.add(i.getTo().toUpperCase());} );
405 } catch(Exception e) {
406 logger.debug("xmlRootElementName: "+xmlRootElementName+"\n"+e);
409 EdgeRuleQuery q1 = new EdgeRuleQuery.Builder(xmlRootElementName).version(v).toOnly().build();
410 Multimap<String, EdgeRule> results = ei.getRules(q1);
411 SortedSet<String> ss=new TreeSet<String>(results.keySet());
412 sbEdge.append(nodeCaption);
413 for(String key : ss) {
414 results.get(key).stream().filter((i) -> (i.getTo().equals(xmlRootElementName) && (! i.isPrivateEdge()))).forEach((i) ->{ sbEdge.append(" - FROM "+i.getFrom()); EdgeDescription ed = new EdgeDescription(i); String footnote = ed.getAlsoDeleteFootnote(xmlRootElementName); sbEdge.append(ed.getRelationshipDescription("FROM", xmlRootElementName)+footnote+"\n"); if(StringUtils.isNotEmpty(footnote)) footnotes.add(footnote);} );
415 results.get(key).stream().filter((i) -> (i.getTo().equals(xmlRootElementName) && (! i.isPrivateEdge()))).forEach((i) ->{ logger.info(new String(new StringBuffer(" - FROM ").append(i.getFrom()).append(i.getDirection().toString()).append(i.getContains())));} );
416 results.get(key).stream().filter((i) -> (i.getTo().equals(xmlRootElementName) && (! i.isPrivateEdge() && i.getPreventDelete().equals("IN")))).forEach((i) ->{ preventDelete.add(i.getFrom().toUpperCase());} );
418 } catch(Exception e) {
419 logger.debug("xmlRootElementName: "+xmlRootElementName+"\n"+e);
421 if(preventDelete.size() > 0) {
422 prevent = xmlRootElementName.toUpperCase()+" cannot be deleted if related to "+String.join(",",preventDelete);
423 logger.debug(prevent);
426 if(StringUtils.isNotEmpty(prevent)) {
427 footnotes.add(prevent);
429 if(footnotes.footnotes.size() > 0) {
430 sbEdge.append(footnotes.toString());
432 validEdges = sbEdge.toString();
434 // Handle description property. Might have a description OR valid edges OR both OR neither.
435 // Only put a description: tag if there is at least one.
436 if (StringUtils.isNotEmpty(pathDescriptionProperty) || StringUtils.isNotEmpty(validEdges) ) {
437 definitionsSb.append(" description: |\n");
438 definitionsLocalSb.append(" description: |\n");
440 if ( pathDescriptionProperty != null ) {
441 definitionsSb.append(" " + pathDescriptionProperty + "\n" );
442 definitionsLocalSb.append(" " + pathDescriptionProperty + "\n" );
444 definitionsSb.append(validEdges);
445 definitionsLocalSb.append(validEdges);
448 if ( requiredCnt > 0 ) {
449 definitionsSb.append(sbRequired);
450 definitionsLocalSb.append(sbRequired);
453 if ( propertyCnt > 0 ) {
454 definitionsSb.append(" properties:\n");
455 definitionsSb.append(sbProperties);
456 if ( !processingInventoryDef) {
457 definitionsLocalSb.append(" properties:\n");
459 definitionsLocalSb.append(sbProperties);
462 namespaceFilter.add(xmlRootElementName);
463 if ( xmlRootElementName.equals("inventory") ) {
464 //will add to javaTypeDefinitions at end
465 inventoryDefSb.append(definitionsLocalSb.toString());
467 javaTypeDefinitions.put(xmlRootElementName, definitionsLocalSb.toString());
469 } catch (Exception e) {
472 if ( xmlRootElementName.equals("inventory") ) {
473 logger.trace("skip xmlRootElementName(2)="+xmlRootElementName);
476 generatedJavaType.put(xmlRootElementName, null);
477 //Write operations by Namespace(tagName)
479 if( validTag(javaTypeName) && javaTypeName == useTag && tag == null) {
480 writeYAMLfile("nodes_"+javaTypeName, getDocumentHeader()+pathSb.toString()+appendDefinitions(namespaceFilter));
481 totalPathSbAccumulator.append(pathSb);
482 pathSb.delete(0, pathSb.length());
483 namespaceFilter.clear();
486 logger.debug("xmlRootElementName(2)="+xmlRootElementName);
490 private void writeYAMLfile(String outfileName, String fileContent) {
491 outfileName = (StringUtils.isEmpty(outfileName)) ? "aai_swagger" : outfileName;
492 outfileName = (outfileName.lastIndexOf(File.separator) == -1) ? yaml_dir + File.separator +outfileName+"_" + v.toString() + "." + generateTypeYAML : outfileName;
493 File outfile = new File(outfileName);
494 File parentDir = outfile.getParentFile();
495 if(parentDir != null && ! parentDir.exists())
498 outfile.createNewFile();
499 } catch (IOException e) {
500 logger.error( "Exception creating output file " + outfileName);
503 Path path = Paths.get(outfileName);
504 Charset charset = Charset.forName("UTF-8");
505 try(BufferedWriter bw = Files.newBufferedWriter(path, charset);) {
506 bw.write(fileContent);
510 } catch ( IOException e) {
511 logger.error( "Exception writing output file " + outfileName);
516 public boolean validTag(String tag) {
522 case "ServiceDesignAndCreation":
524 case "LicenseManagement":
525 case "CloudInfrastructure":
532 public String appendOperations() {
534 StringBuffer sb = new StringBuffer();
535 Map<String, String> sortedOperationDefinitions = new TreeMap<String, String>(operationDefinitions);
536 for (Map.Entry<String, String> entry : sortedOperationDefinitions.entrySet()) {
537 sb.append(entry.getValue());
539 return sb.toString();