Update from titan to using janusgraph
[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 ( opId == null || !opId.contains(addType)) {
291                                 processJavaTypeElementSwagger( addType, getJavaTypeElementSwagger(addType), 
292                                         pathSb, definitionsSb, path,  tag == null ? useTag : tag, useOpId, null,
293                                         newPathParams, validEdges);
294                         }
295                         // need item name of array
296                                 String itemName = processJavaTypeElementSwagger( addType, getJavaTypeElementSwagger(addType), 
297                                         pathSb, definitionsSb, path,  tag == null ? useTag : tag, useOpId, 
298                                                         "array", null, null );
299                                         
300                                 if ( itemName != null ) {
301                                         if ( addType.equals("AaiInternal") ) {
302                                                 logger.debug( "addType AaiInternal, skip properties");
303                                                         
304                                         } else if ( getItemName == null) {
305                                                 ++propertyCnt;
306                                                 sbProperties.append("      " + getXmlRootElementName(addType) + ":\n");
307                                                 sbProperties.append("        type: array\n        items:\n");
308                                                 sbProperties.append("          $ref: \"#/definitions/" + (itemName == "" ? "aai-internal" : itemName) + "\"\n");
309                                                 if ( StringUtils.isNotEmpty(elementDescription) )
310                                                         sbProperties.append("        description: " + elementDescription + "\n");
311                                         }
312                                 } else {
313                                         if ( ("java.util.ArrayList").equals(xmlElementElement.getAttribute("container-type"))) {
314                                                         // need properties for getXmlRootElementName(addType)
315                                                 newPathParams = new StringBuffer((pathParams == null ? "" : pathParams.toString())+sbParameters.toString()); 
316                                                 processJavaTypeElementSwagger( addType, getJavaTypeElementSwagger(addType), 
317                                                         pathSb, definitionsSb, path,  tag == null ? useTag : tag, useOpId, 
318                                                                         null, newPathParams, validEdges );
319                                                 sbProperties.append("      " + getXmlRootElementName(addType) + ":\n");
320                                                 sbProperties.append("        type: array\n        items:          \n");
321                                                 sbProperties.append("          $ref: \"#/definitions/" + getXmlRootElementName(addType) + "\"\n");
322                                                 if ( StringUtils.isNotEmpty(elementDescription) )
323                                                         sbProperties.append("        description: " + elementDescription + "\n");
324
325                                         } else {
326                                                 if(addType.equals("AaiInternal"))  //Filter out references to AaiInternal
327                                                         sbProperties.append("");
328                                                 else {
329                                                         sbProperties.append("      " + getXmlRootElementName(addType) + ":\n");
330                                                         sbProperties.append("        type: object\n");
331                                                         sbProperties.append("        $ref: \"#/definitions/" + getXmlRootElementName(addType) + "\"\n");
332                                                 }
333                                         }
334                                         if ( StringUtils.isNotEmpty(elementDescription) )
335                                                 sbProperties.append("        description: " + elementDescription + "\n");
336                                         ++propertyCnt;
337                                 }
338                 }
339                 }
340                 
341                 if ( sbParameters.toString().length() > 0 ) {
342                         if ( pathParams == null )
343                                 pathParams = new StringBuffer();
344                         pathParams.append(sbParameters);
345                 }
346                 GetOperation get = new GetOperation(useOpId, xmlRootElementName, tag, path,  pathParams == null ? "" : pathParams.toString());
347             pathSb.append(get.toString());
348             logger.debug("opId vs useOpId:"+opId+" vs "+useOpId+" PathParams="+pathParams);
349                 // add PUT
350                 PutOperation put = new PutOperation(useOpId, xmlRootElementName, tag, path, pathParams == null ? "" : pathParams.toString(), this.v);
351                 pathSb.append(put.toString());
352                 // add PATCH
353                 PatchOperation patch = new PatchOperation(useOpId, xmlRootElementName, tag, path, pathParams == null ? "" : pathParams.toString());
354                 pathSb.append(patch.toString());
355                 // add DELETE
356                 DeleteOperation del = new DeleteOperation(useOpId, xmlRootElementName, tag, path, pathParams == null ? "" : pathParams.toString());
357                 pathSb.append(del.toString());
358                 //Write operations by Namespace(tagName)
359 //              if(javaTypeName == useTag && tag == null) {
360 //                      pathSb.append(appendDefinitions());
361 //                      writeYAMLfile(javaTypeName, pathSb.toString());
362 //                      pathSb.delete(0, pathSb.length());
363 //                      javaTypeDefinitions.clear();
364 //                      generatedJavaType.clear();
365 //              }
366                 if ( generatedJavaType.containsKey(xmlRootElementName) ) {
367                         logger.debug("xmlRootElementName(1)="+xmlRootElementName);
368                         return null;
369                 }
370         
371                 definitionsSb.append("  " + xmlRootElementName + ":\n");
372                 definitionsLocalSb.append("  " + xmlRootElementName + ":\n");
373                 Collection<EdgeDescription> edges = edgeRuleSet.getEdgeRules(xmlRootElementName );
374                 DeleteFootnoteSet footnotes = new DeleteFootnoteSet(xmlRootElementName);
375                 if ( edges.size() > 0 ) {
376                         StringBuffer sbEdge = new StringBuffer();
377                         sbEdge.append("      ###### Related Nodes\n");
378                         
379                         for (EdgeDescription ed : edges) {
380                                 if ( ed.getRuleKey().startsWith(xmlRootElementName)) {
381                                     sbEdge.append("      - TO ").append(ed.getRuleKey().substring(ed.getRuleKey().indexOf("|")+1));
382                                     String footnote = ed.getAlsoDeleteFootnote(xmlRootElementName);
383                                     sbEdge.append(ed.getRelationshipDescription("TO", xmlRootElementName)+footnote+"\n");                                   
384                                     if(StringUtils.isNotEmpty(footnote)) footnotes.add(footnote);
385                                 }
386                         }
387                         for (EdgeDescription ed : edges) { 
388                                 if ( ed.getRuleKey().endsWith(xmlRootElementName)) {
389                                     sbEdge.append("      - FROM ").append(ed.getRuleKey().substring(0, ed.getRuleKey().indexOf("|")));
390                                     String footnote = ed.getAlsoDeleteFootnote(xmlRootElementName);
391                                     sbEdge.append(ed.getRelationshipDescription("FROM", xmlRootElementName)+footnote+"\n");                                 
392                                     if(StringUtils.isNotEmpty(footnote)) footnotes.add(footnote);
393                                 }
394                         }
395                         footnotes.add(edgeRuleSet.preventDeleteRules(xmlRootElementName));
396                         sbEdge.append(footnotes.toString());
397                         validEdges = sbEdge.toString();
398                 }
399
400                 // Handle description property.  Might have a description OR valid edges OR both OR neither.
401                 // Only put a description: tag if there is at least one.
402                 if (pathDescriptionProperty != null || validEdges != null) {
403                         definitionsSb.append("    description: |\n");
404                         definitionsLocalSb.append("    description: |\n");      
405
406                         if ( pathDescriptionProperty != null ) {
407                                 definitionsSb.append("      " + pathDescriptionProperty + "\n" );
408                                 definitionsLocalSb.append("      " + pathDescriptionProperty    + "\n" );
409                         }
410                         if (validEdges != null) {
411                                 definitionsSb.append(validEdges);
412                                 definitionsLocalSb.append(validEdges);
413                         }
414                 }
415                 
416                 if ( requiredCnt > 0 ) {
417                         definitionsSb.append(sbRequired);
418                         definitionsLocalSb.append(sbRequired);
419                 }
420                         
421                 if ( propertyCnt > 0 ) {
422                         definitionsSb.append("    properties:\n");
423                         definitionsSb.append(sbProperties);
424                         definitionsLocalSb.append("    properties:\n");
425                         definitionsLocalSb.append(sbProperties);
426                 }
427                 try {
428                         javaTypeDefinitions.put(xmlRootElementName, definitionsLocalSb.toString());
429                 } catch (Exception e) {
430                         e.printStackTrace();
431                 }
432                 generatedJavaType.put(xmlRootElementName, null);
433                 logger.trace("xmlRootElementName(2)="+xmlRootElementName);
434                 return null;
435         }
436         
437         private void writeYAMLfile(String outfileName, String fileContent) {
438                 outfileName = (StringUtils.isEmpty(outfileName)) ? "aai_swagger" : outfileName;
439                 outfileName = (outfileName.lastIndexOf(File.separator) == -1) ? yaml_dir + File.separator +outfileName+"_" + v.name() + "." + generateTypeYAML : outfileName;
440                 File outfile = new File(outfileName);
441                 File parentDir = outfile.getParentFile();
442                 if(parentDir != null && ! parentDir.exists()) 
443                         parentDir.mkdirs();
444                 try {
445                         outfile.createNewFile();
446                 } catch (IOException e) {
447                         logger.error( "Exception creating output file " + outfileName);
448                         e.printStackTrace();
449                 }
450                 BufferedWriter bw = null;
451                 try {
452                         Charset charset = Charset.forName("UTF-8");
453                         Path path = Paths.get(outfileName);
454                         bw = Files.newBufferedWriter(path, charset);
455                         bw.write(fileContent);
456                         if ( bw != null ) {
457                                 bw.close();
458                         }
459                 } catch ( IOException e) {
460                         logger.error( "Exception writing output file " + outfileName);
461                         e.printStackTrace();
462                 } 
463         }
464 }