ff7d44b64d157d3c463cdcd577b11694c6ffc113
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / util / genxsd / YAMLfromOXM.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
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
10  *
11  *    http://www.apache.org/licenses/LICENSE-2.0
12  *
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=========================================================
19  */
20 package org.onap.aai.util.genxsd;
21
22 import org.apache.commons.lang3.StringUtils;
23 import org.onap.aai.exceptions.AAIException;
24 import org.onap.aai.introspection.Version;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27 import org.w3c.dom.Element;
28 import org.w3c.dom.NodeList;
29 import org.xml.sax.SAXException;
30
31 import javax.xml.parsers.ParserConfigurationException;
32 import java.io.BufferedWriter;
33 import java.io.File;
34 import java.io.FileNotFoundException;
35 import java.io.IOException;
36 import java.nio.charset.Charset;
37 import java.nio.file.Files;
38 import java.nio.file.Path;
39 import java.nio.file.Paths;
40 import java.util.*;
41
42 public class YAMLfromOXM extends OxmFileProcessor {
43         private static final Logger logger = LoggerFactory.getLogger("GenerateXsd.class");
44         private static final String root = "../aai-schema/src/main/resources";
45         private static final String autoGenRoot = "aai-schema/src/main/resources";
46         private static final String generateTypeYAML = "yaml";
47         private static final String normalStartDir = "aai-core";
48         private static final String yaml_dir = (((System.getProperty("user.dir") != null) && (!System.getProperty("user.dir").contains(normalStartDir))) ? autoGenRoot : root) + "/aai_swagger_yaml";
49
50         private File edgeFile;
51         private EdgeRuleSet edgeRuleSet = null;
52         public YAMLfromOXM(File oxmFile, Version v, File edgeFile) throws ParserConfigurationException, SAXException, IOException, AAIException, FileNotFoundException {
53                 super(oxmFile, v);
54                 this.edgeFile = edgeFile;
55                 init();
56         }
57         public YAMLfromOXM(String xml, Version v, File edgeFile) throws ParserConfigurationException, SAXException, IOException, AAIException, FileNotFoundException {
58                 super(xml, v);
59                 this.edgeFile = edgeFile;
60                 init();
61         }
62         
63         @Override
64         public String getDocumentHeader() {
65                 StringBuffer sb = new StringBuffer();
66                 sb.append("swagger: \"2.0\"\ninfo:\n  ");
67                 sb.append("description: |");
68                 sb.append("\n\n    [Differences versus the previous schema version]("+"apidocs/aai_swagger_" + v.name() + ".diff)");
69                 sb.append("\n\n    Copyright © 2017 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    ECOMP and OpenECOMP are trademarks and service marks of AT&T Intellectual Property.\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.name() +"\"\n");
70                 sb.append("  title: Active and Available Inventory REST API\n");
71                 sb.append("  license:\n    name: Apache 2.0\n    url: http://www.apache.org/licenses/LICENSE-2.0.html\n");
72                 sb.append("  contact:\n    name:\n    url:\n    email:\n");
73                 sb.append("host:\nbasePath: /aai/" + v.name() + "\n");
74                 sb.append("schemes:\n  - https\npaths:\n");
75                 return sb.toString();
76         }
77         
78         protected void init() throws ParserConfigurationException, SAXException, IOException, AAIException, FileNotFoundException {
79                 super.init();
80                 edgeRuleSet = new EdgeRuleSet(edgeFile);
81         }
82
83         @Override
84         public String process() throws AAIException {
85                 StringBuffer sb = new StringBuffer();
86                 EdgeRuleSet edgeRuleSet = null;
87                 try {
88                         edgeRuleSet = new EdgeRuleSet(edgeFile);
89                 } catch (Exception e) {
90                         logger.warn("No valid Edge Rule Set available("+edgeFile+"): "+e.getMessage());
91                 }
92
93                 StringBuffer pathSb = new StringBuffer();
94                 pathSb.append(getDocumentHeader());
95                 StringBuffer definitionsSb = new StringBuffer();
96                 for ( int i = 0; i < javaTypeNodes.getLength(); ++ i ) {
97                         XSDElement javaTypeElement = new XSDElement((Element)javaTypeNodes.item(i));
98                         logger.debug("External: "+javaTypeElement.getAttribute("name"));
99                         String javaTypeName = javaTypeElement.name();
100                         if ( javaTypeName == null ) {
101                                 String msg = "Invalid OXM file: <java-type> has no name attribute in " + oxmFile; 
102                                 logger.error(msg);
103                                 throw new AAIException(msg);
104                         }
105                         //Skip any type that has already been processed(recursion could be the reason)
106                         if ( generatedJavaType.containsKey(getXmlRootElementName(javaTypeName)) ) {
107                                         continue;
108                         }
109                         processJavaTypeElementSwagger( javaTypeName, javaTypeElement, pathSb,
110                                 definitionsSb, null, null, null, null, null, null);
111                 }
112                 sb.append(pathSb);
113
114                 sb.append(appendDefinitions());
115                 PutRelationPathSet prp = new PutRelationPathSet(v);
116                 prp.generateRelations(edgeRuleSet);
117                 return sb.toString();
118         }
119         
120         public String appendDefinitions() {
121                 //append definitions
122                 StringBuffer sb = new StringBuffer("definitions:\n");
123                 Map<String, String> sortedJavaTypeDefinitions = new TreeMap<String, String>(javaTypeDefinitions);
124                 for (Map.Entry<String, String> entry : sortedJavaTypeDefinitions.entrySet()) {
125                     logger.debug("Key: "+entry.getKey()+"Test: "+ (entry.getKey() == "relationship"));  
126                     if(entry.getKey().matches("relationship")) {
127                             String jb=entry.getValue();
128                         logger.debug("Value: "+jb);
129                             int ndx=jb.indexOf("related-to-property:");
130                             if(ndx > 0) {
131                                 jb=jb.substring(0, ndx);
132                                 jb=jb.replaceAll(" +$", "");
133                             }
134                         logger.debug("Value-after: "+jb);
135                         sb.append(jb);
136                         continue;
137                     }
138                     sb.append(entry.getValue());
139                 }
140                 
141                 sb.append("patchDefinitions:\n");
142                 for (Map.Entry<String, String> entry : sortedJavaTypeDefinitions.entrySet()) {
143                     String jb=entry.getValue().replaceAll("/definitions/", "/patchDefinitions/");
144                     int ndx=jb.indexOf("relationship-list:");
145                     if(ndx > 0) {
146                         jb=jb.substring(0, ndx);
147                         jb=jb.replaceAll(" +$", "");
148                     }
149                     int ndx1=jb.indexOf("resource-version:");
150                         logger.debug("Key: "+entry.getKey()+" index: " + ndx1);                 
151                         logger.debug("Value: "+jb);                     
152                         if(ndx1 > 0) {
153                             jb=jb.substring(0, ndx1);
154                             jb=jb.replaceAll(" +$", "");
155                     }
156                         logger.debug("Value-after: "+jb);
157                     sb.append(jb);
158                 }
159                     
160                 sb.append("getDefinitions:\n");
161                 for (Map.Entry<String, String> entry : sortedJavaTypeDefinitions.entrySet()) {
162                     String jb=entry.getValue().replaceAll("/definitions/", "/getDefinitions/");
163                     sb.append(jb);
164                 }
165                 return sb.toString();
166         }
167         
168         private String processJavaTypeElementSwagger( String javaTypeName, Element javaTypeElement,
169                         StringBuffer pathSb, StringBuffer definitionsSb, String path, String tag, String opId,
170                         String getItemName, StringBuffer pathParams, String validEdges) {
171                 
172                 String xmlRootElementName = getXMLRootElementName(javaTypeElement);
173                 StringBuilder definitionsLocalSb = new StringBuilder(256);
174                 
175                 String useTag = null;
176                 String useOpId = null;
177                 logger.debug("tag="+tag);
178                 if ( tag != null ) {
179                         switch ( tag ) {
180                         case "Network":
181                         case "ServiceDesignAndCreation":
182                         case "Business":
183                         case "LicenseManagement":
184                         case "CloudInfrastructure":
185                                 break;
186                         default:
187                                 logger.debug("javaTypeName="+javaTypeName);
188                                 return null;
189                         }
190                 }
191                 
192                 if ( !javaTypeName.equals("Inventory") ) {
193                         if ( javaTypeName.equals("AaiInternal"))
194                                 return null;
195                         if ( opId == null )
196                                 useOpId = javaTypeName;
197                         else
198                                 useOpId = opId + javaTypeName;
199                         if ( tag == null )
200                                 useTag = javaTypeName;
201                 }
202
203                 path = xmlRootElementName.equals("inventory") ? "" : (path == null) ? "/" + xmlRootElementName : path + "/" + xmlRootElementName;
204                 XSDJavaType javaType = new XSDJavaType(javaTypeElement);
205                 if ( getItemName != null) {
206                 if ( getItemName.equals("array") )
207                         return javaType.getArrayType();
208                 else
209                         return javaType.getItemName();
210                 }
211                         
212                 NodeList parentNodes = javaTypeElement.getElementsByTagName("java-attributes");
213                 if ( parentNodes.getLength() == 0 ) {
214                         logger.debug( "no java-attributes for java-type " + javaTypeName);
215                         return "";
216                 }
217
218                 String pathDescriptionProperty = javaType.getPathDescriptionProperty();
219                 String container = javaType.getContainerProperty();
220                 Vector<String> indexedProps = javaType.getIndexedProps();
221                 Vector<String> containerProps = new Vector<String>();
222                 if(container != null) {
223                         logger.debug("javaTypeName " + javaTypeName + " container:" + container +" indexedProps:"+indexedProps);
224                 }
225                 
226                 Element parentElement = (Element)parentNodes.item(0);
227                 NodeList xmlElementNodes = parentElement.getElementsByTagName("xml-element");
228
229                 StringBuffer sbParameters = new StringBuffer();
230                 StringBuffer sbRequired = new StringBuffer();
231                 int requiredCnt = 0;
232                 int propertyCnt = 0;
233                 StringBuffer sbProperties = new StringBuffer();
234                 
235                 if ( appliedPaths.containsKey(path)) 
236                         return null;
237                 
238                 StringTokenizer st = new StringTokenizer(path, "/");
239                 logger.debug("path: " + path + " st? " + st.toString());
240                 if ( st.countTokens() > 1 && getItemName == null ) {
241                         logger.debug("appliedPaths: " + appliedPaths + " containsKey? " + appliedPaths.containsKey(path));
242                         appliedPaths.put(path, xmlRootElementName);
243                 }
244
245                 Vector<String> addTypeV = null;
246                 for ( int i = 0; i < xmlElementNodes.getLength(); ++i ) {
247                                 XSDElement xmlElementElement = new XSDElement((Element)xmlElementNodes.item(i));
248                                 if ( !xmlElementElement.getParentNode().isSameNode(parentElement))
249                                         continue;
250                                 String elementDescription=xmlElementElement.getPathDescriptionProperty();
251                                 if(getItemName == null) {
252                                         addTypeV = xmlElementElement.getAddTypes(v.name());
253                                 }
254                     if ( "true".equals(xmlElementElement.getAttribute("xml-key"))) {
255                         path += "/{" + xmlElementElement.getAttribute("name") + "}";
256                     }
257                     logger.debug("path: " + path);
258                 logger.debug( "xmlElementElement.getAttribute(required):"+xmlElementElement.getAttribute("required") );
259                                 
260                                 if ( ("true").equals(xmlElementElement.getAttribute("required"))) {
261                                         if ( requiredCnt == 0 )
262                                                 sbRequired.append("    required:\n");
263                                         ++requiredCnt;
264                                         if ( addTypeV == null || addTypeV.isEmpty()) {
265                                                 sbRequired.append("    - " + xmlElementElement.getAttribute("name") + "\n");
266                                         } else { 
267                                                 for ( int k = 0; k < addTypeV.size(); ++k ) {
268                                                         sbRequired.append("    - " + getXmlRootElementName(addTypeV.elementAt(k)) + ":\n");
269                                                 }
270                                         }
271                                 }
272
273                                 if ( "true".equals(xmlElementElement.getAttribute("xml-key")) )  {
274                                         sbParameters.append(xmlElementElement.getPathParamYAML(elementDescription));
275                                 }
276                                 if (  indexedProps != null
277                                                 && indexedProps.contains(xmlElementElement.getAttribute("name") ) ) {
278                                         containerProps.add(xmlElementElement.getQueryParamYAML());
279                                         GetOperation.addContainerProps(container, containerProps);
280                                 }
281                         if ( xmlElementElement.isStandardType()) {
282                                 sbProperties.append(xmlElementElement.getTypePropertyYAML());
283                                 ++propertyCnt;
284                         }
285                         
286                         StringBuffer newPathParams = new StringBuffer((pathParams == null ? "" : pathParams.toString())+sbParameters.toString()); 
287                 for ( int k = 0; addTypeV != null && k < addTypeV.size(); ++k ) {
288                         String addType = addTypeV.elementAt(k);
289                                 logger.debug("addType: "+ addType);
290                                 if(addType == "overloaded-model" || addType.equals("OverloadedModel") || addType == "owning-entity") {
291                                         logger.debug("Description check: "+ addType+"opId="+opId+" itemName=none");
292 //                                      Log.info("Element name="+xmlElementElement.getAttribute("name"));
293                                 }
294
295                 
296                         if ( opId == null || !opId.contains(addType)) {
297                                 processJavaTypeElementSwagger( addType, getJavaTypeElementSwagger(addType), 
298                                         pathSb, definitionsSb, path,  tag == null ? useTag : tag, useOpId, null,
299                                         newPathParams, validEdges);
300                         }
301                         // need item name of array
302                                 String itemName = processJavaTypeElementSwagger( addType, getJavaTypeElementSwagger(addType), 
303                                         pathSb, definitionsSb, path,  tag == null ? useTag : tag, useOpId, 
304                                                         "array", null, null );
305                                         
306                                 if ( itemName != null ) {
307                                         if ( addType.equals("AaiInternal") ) {
308                                                 logger.debug( "addType AaiInternal, skip properties");
309                                                         
310                                         } else if ( getItemName == null) {
311                                                 ++propertyCnt;
312                                                 sbProperties.append("      " + getXmlRootElementName(addType) + ":\n");
313                                                 sbProperties.append("        type: array\n        items:\n");
314                                                 sbProperties.append("          $ref: \"#/definitions/" + (itemName == "" ? "aai-internal" : itemName) + "\"\n");
315                                                 if ( StringUtils.isNotEmpty(elementDescription) )
316                                                         sbProperties.append("        description: " + elementDescription + "\n");
317                                         }
318                                 } else {
319                                         if ( ("java.util.ArrayList").equals(xmlElementElement.getAttribute("container-type"))) {
320                                                         // need properties for getXmlRootElementName(addType)
321                                                 newPathParams = new StringBuffer((pathParams == null ? "" : pathParams.toString())+sbParameters.toString()); 
322                                                 processJavaTypeElementSwagger( addType, getJavaTypeElementSwagger(addType), 
323                                                         pathSb, definitionsSb, path,  tag == null ? useTag : tag, useOpId, 
324                                                                         null, newPathParams, validEdges );
325                                                 sbProperties.append("      " + getXmlRootElementName(addType) + ":\n");
326                                                 sbProperties.append("        type: array\n        items:          \n");
327                                                 sbProperties.append("          $ref: \"#/definitions/" + getXmlRootElementName(addType) + "\"\n");
328                                                 if ( StringUtils.isNotEmpty(elementDescription) )
329                                                         sbProperties.append("        description: " + elementDescription + "\n");
330
331                                         } else {
332                                                 if(addType.equals("AaiInternal"))  //Filter out references to AaiInternal
333                                                         sbProperties.append("");
334                                                 else {
335                                                         sbProperties.append("      " + getXmlRootElementName(addType) + ":\n");
336                                                         sbProperties.append("        type: object\n");
337                                                         sbProperties.append("        $ref: \"#/definitions/" + getXmlRootElementName(addType) + "\"\n");
338                                                 }
339                                         }
340                                         if ( StringUtils.isNotEmpty(elementDescription) )
341                                                 sbProperties.append("        description: " + elementDescription + "\n");
342                                         ++propertyCnt;
343                                 }
344                 }
345                 }
346                 
347                 if ( sbParameters.toString().length() > 0 ) {
348                         if ( pathParams == null )
349                                 pathParams = new StringBuffer();
350                         pathParams.append(sbParameters);
351                 }
352                 GetOperation get = new GetOperation(useOpId, xmlRootElementName, tag, path,  pathParams == null ? "" : pathParams.toString());
353             pathSb.append(get.toString());
354             logger.debug("opId vs useOpId:"+opId+" vs "+useOpId+" PathParams="+pathParams);
355                 // add PUT
356                 PutOperation put = new PutOperation(useOpId, xmlRootElementName, tag, path, pathParams == null ? "" : pathParams.toString(), this.v);
357                 pathSb.append(put.toString());
358                 // add PATCH
359                 PatchOperation patch = new PatchOperation(useOpId, xmlRootElementName, tag, path, pathParams == null ? "" : pathParams.toString());
360                 pathSb.append(patch.toString());
361                 // add DELETE
362                 DeleteOperation del = new DeleteOperation(useOpId, xmlRootElementName, tag, path, pathParams == null ? "" : pathParams.toString());
363                 pathSb.append(del.toString());
364                 //Write operations by Namespace(tagName)
365 //              if(javaTypeName == useTag && tag == null) {
366 //                      pathSb.append(appendDefinitions());
367 //                      writeYAMLfile(javaTypeName, pathSb.toString());
368 //                      pathSb.delete(0, pathSb.length());
369 //                      javaTypeDefinitions.clear();
370 //                      generatedJavaType.clear();
371 //              }
372                 if ( generatedJavaType.containsKey(xmlRootElementName) ) {
373                         logger.debug("xmlRootElementName(1)="+xmlRootElementName);
374                         return null;
375                 }
376         
377                 definitionsSb.append("  " + xmlRootElementName + ":\n");
378                 definitionsLocalSb.append("  " + xmlRootElementName + ":\n");
379                 Collection<EdgeDescription> edges = edgeRuleSet.getEdgeRules(xmlRootElementName );
380                 DeleteFootnoteSet footnotes = new DeleteFootnoteSet(xmlRootElementName);
381                 if ( edges.size() > 0 ) {
382                         StringBuffer sbEdge = new StringBuffer();
383                         sbEdge.append("      ###### Related Nodes\n");
384                         
385                         for (EdgeDescription ed : edges) {
386                                 if ( ed.getRuleKey().startsWith(xmlRootElementName)) {
387                                     sbEdge.append("      - TO ").append(ed.getRuleKey().substring(ed.getRuleKey().indexOf("|")+1));
388                                     String footnote = ed.getAlsoDeleteFootnote(xmlRootElementName);
389                                     sbEdge.append(ed.getRelationshipDescription("TO", xmlRootElementName)+footnote+"\n");                                   
390                                     if(StringUtils.isNotEmpty(footnote)) footnotes.add(footnote);
391                                 }
392                         }
393                         for (EdgeDescription ed : edges) { 
394                                 if ( ed.getRuleKey().endsWith(xmlRootElementName)) {
395                                     sbEdge.append("      - FROM ").append(ed.getRuleKey().substring(0, ed.getRuleKey().indexOf("|")));
396                                     String footnote = ed.getAlsoDeleteFootnote(xmlRootElementName);
397                                     sbEdge.append(ed.getRelationshipDescription("FROM", xmlRootElementName)+footnote+"\n");                                 
398                                     if(StringUtils.isNotEmpty(footnote)) footnotes.add(footnote);
399                                 }
400                         }
401                         footnotes.add(edgeRuleSet.preventDeleteRules(xmlRootElementName));
402                         sbEdge.append(footnotes.toString());
403                         validEdges = sbEdge.toString();
404                 }
405
406                 // Handle description property.  Might have a description OR valid edges OR both OR neither.
407                 // Only put a description: tag if there is at least one.
408                 if (pathDescriptionProperty != null || validEdges != null) {
409                         definitionsSb.append("    description: |\n");
410                         definitionsLocalSb.append("    description: |\n");      
411
412                         if ( pathDescriptionProperty != null ) {
413                                 definitionsSb.append("      " + pathDescriptionProperty + "\n" );
414                                 definitionsLocalSb.append("      " + pathDescriptionProperty    + "\n" );
415                         }
416                         if (validEdges != null) {
417                                 definitionsSb.append(validEdges);
418                                 definitionsLocalSb.append(validEdges);
419                         }
420                 }
421                 
422                 if ( requiredCnt > 0 ) {
423                         definitionsSb.append(sbRequired);
424                         definitionsLocalSb.append(sbRequired);
425                 }
426                         
427                 if ( propertyCnt > 0 ) {
428                         definitionsSb.append("    properties:\n");
429                         definitionsSb.append(sbProperties);
430                         definitionsLocalSb.append("    properties:\n");
431                         definitionsLocalSb.append(sbProperties);
432                 }
433                 try {
434                         javaTypeDefinitions.put(xmlRootElementName, definitionsLocalSb.toString());
435                 } catch (Exception e) {
436                         e.printStackTrace();
437                 }
438                 generatedJavaType.put(xmlRootElementName, null);
439                 logger.trace("xmlRootElementName(2)="+xmlRootElementName);
440                 return null;
441         }
442         
443         private void writeYAMLfile(String outfileName, String fileContent) {
444                 outfileName = (StringUtils.isEmpty(outfileName)) ? "aai_swagger" : outfileName;
445                 outfileName = (outfileName.lastIndexOf(File.separator) == -1) ? yaml_dir + File.separator +outfileName+"_" + v.name() + "." + generateTypeYAML : outfileName;
446                 File outfile = new File(outfileName);
447                 File parentDir = outfile.getParentFile();
448                 if(parentDir != null && ! parentDir.exists()) 
449                         parentDir.mkdirs();
450                 try {
451                         outfile.createNewFile();
452                 } catch (IOException e) {
453                         logger.error( "Exception creating output file " + outfileName);
454                         e.printStackTrace();
455                 }
456                 BufferedWriter bw = null;
457                 try {
458                         Charset charset = Charset.forName("UTF-8");
459                         Path path = Paths.get(outfileName);
460                         bw = Files.newBufferedWriter(path, charset);
461                         bw.write(fileContent);
462                         if ( bw != null ) {
463                                 bw.close();
464                         }
465                 } catch ( IOException e) {
466                         logger.error( "Exception writing output file " + outfileName);
467                         e.printStackTrace();
468                 } 
469         }
470 }