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<String, String>();
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();
120 for ( int i = 0; i < javaTypeNodes.getLength(); ++ i ) {
121 XSDElement javaTypeElement = new XSDElement((Element)javaTypeNodes.item(i));
122 String javaTypeName = javaTypeElement.name();
124 logger.debug("External: "+javaTypeElement.getAttribute("name")+"/"+getXmlRootElementName(javaTypeName));
125 if ( javaTypeName == null ) {
126 String msg = "Invalid OXM file: <java-type> has no name attribute in " + oxmFile;
128 throw new AAIException(msg);
130 namespaceFilter.add(getXmlRootElementName(javaTypeName));
131 //Skip any type that has already been processed(recursion could be the reason)
132 if ( generatedJavaType.containsKey(getXmlRootElementName(javaTypeName)) ) {
135 processJavaTypeElementSwagger( javaTypeName, javaTypeElement, pathSb,
136 definitionsSb, null, null, null, null, null, null);
139 // sb.append(getDocumentHeader());
140 // sb.append(totalPathSbAccumulator);
141 sb.append(appendOperations());
142 sb.append(appendDefinitions());
143 PutRelationPathSet prp = new PutRelationPathSet(v);
144 prp.generateRelations(ei);
145 return sb.toString();
148 public String appendDefinitions() {
149 return appendDefinitions(null);
152 public String appendDefinitions(Set<String> namespaceFilter) {
153 if ( inventoryDefSb != null ) {
154 javaTypeDefinitions.put("inventory", inventoryDefSb.toString());
156 StringBuffer sb = new StringBuffer("definitions:\n");
157 Map<String, String> sortedJavaTypeDefinitions = new TreeMap<String, String>(javaTypeDefinitions);
159 for (Map.Entry<String, String> entry : sortedJavaTypeDefinitions.entrySet()) {
160 if(namespaceFilter != null && (! namespaceFilter.contains(entry.getKey()))) {
163 sb.append(entry.getValue());
165 return sb.toString();
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) {
172 String xmlRootElementName = getXMLRootElementName(javaTypeElement);
173 StringBuilder definitionsLocalSb = new StringBuilder(256);
175 String useTag = null;
176 String useOpId = null;
177 logger.debug("tag="+tag);
179 if(tag != null && (! validTag(tag))) {
180 logger.debug("tag="+tag+"; javaTypeName="+javaTypeName);
183 if ( !javaTypeName.equals("Inventory") ) {
184 if ( javaTypeName.equals("AaiInternal"))
187 useOpId = javaTypeName;
189 useOpId = opId + javaTypeName;
191 useTag = javaTypeName;
194 path = xmlRootElementName.equals("inventory") ? "" : (path == null) ? "/" + xmlRootElementName : path + "/" + xmlRootElementName;
195 XSDJavaType javaType = new XSDJavaType(javaTypeElement);
196 if ( getItemName != null) {
197 if ( getItemName.equals("array") )
198 return javaType.getArrayType();
200 return javaType.getItemName();
203 NodeList parentNodes = javaTypeElement.getElementsByTagName("java-attributes");
204 if ( parentNodes.getLength() == 0 ) {
205 logger.debug( "no java-attributes for java-type " + javaTypeName);
209 String pathDescriptionProperty = javaType.getPathDescriptionProperty();
210 String container = javaType.getContainerProperty();
211 Vector<String> indexedProps = javaType.getIndexedProps();
212 Vector<String> containerProps = new Vector<String>();
213 if(container != null) {
214 logger.debug("javaTypeName " + javaTypeName + " container:" + container +" indexedProps:"+indexedProps);
217 Element parentElement = (Element)parentNodes.item(0);
218 NodeList xmlElementNodes = parentElement.getElementsByTagName("xml-element");
220 StringBuffer sbParameters = new StringBuffer();
221 StringBuffer sbRequired = new StringBuffer();
224 StringBuffer sbProperties = new StringBuffer();
226 if ( appliedPaths.containsKey(path))
229 StringTokenizer st = new StringTokenizer(path, "/");
230 logger.debug("path: " + path + " st? " + st.toString());
231 if ( st.countTokens() > 1 && getItemName == null ) {
232 logger.debug("appliedPaths: " + appliedPaths + " containsKey? " + appliedPaths.containsKey(path));
233 appliedPaths.put(path, xmlRootElementName);
235 Vector<String> addTypeV = null;
236 for ( int i = 0; i < xmlElementNodes.getLength(); ++i ) {
237 XSDElement xmlElementElement = new XSDElement((Element)xmlElementNodes.item(i));
238 if ( !xmlElementElement.getParentNode().isSameNode(parentElement))
240 String elementDescription=xmlElementElement.getPathDescriptionProperty();
241 if(getItemName == null) {
242 addTypeV = xmlElementElement.getAddTypes(v.toString());
244 if ( "true".equals(xmlElementElement.getAttribute("xml-key"))) {
245 path += "/{" + xmlElementElement.getAttribute("name") + "}";
247 logger.debug("path: " + path);
248 logger.debug( "xmlElementElement.getAttribute(required):"+xmlElementElement.getAttribute("required") );
250 if ( ("true").equals(xmlElementElement.getAttribute("required"))) {
251 if ( requiredCnt == 0 )
252 sbRequired.append(" required:\n");
254 if ( addTypeV == null || addTypeV.isEmpty()) {
255 sbRequired.append(" - " + xmlElementElement.getAttribute("name") + "\n");
257 for ( int k = 0; k < addTypeV.size(); ++k ) {
258 sbRequired.append(" - " + getXmlRootElementName(addTypeV.elementAt(k)) + ":\n");
263 if ( "true".equals(xmlElementElement.getAttribute("xml-key")) ) {
264 sbParameters.append(xmlElementElement.getPathParamYAML(elementDescription));
266 if ( indexedProps != null
267 && indexedProps.contains(xmlElementElement.getAttribute("name") ) ) {
268 containerProps.add(xmlElementElement.getQueryParamYAML());
269 NodeGetOperation.addContainerProps(container, containerProps);
271 if ( xmlElementElement.isStandardType()) {
272 sbProperties.append(xmlElementElement.getTypePropertyYAML());
276 // 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
277 StringBuffer newPathParams = new StringBuffer(sbParameters.toString());
278 for ( int k = 0; addTypeV != null && k < addTypeV.size(); ++k ) {
279 String addType = addTypeV.elementAt(k);
280 namespaceFilter.add(getXmlRootElementName(addType));
281 if ( opId == null || !opId.contains(addType)) {
282 processJavaTypeElementSwagger( addType, getJavaTypeElementSwagger(addType),
283 pathSb, definitionsSb, path, tag == null ? useTag : tag, useOpId, null,
284 newPathParams, validEdges);
286 // need item name of array
287 String itemName = processJavaTypeElementSwagger( addType, getJavaTypeElementSwagger(addType),
288 pathSb, definitionsSb, path, tag == null ? useTag : tag, useOpId,
289 "array", null, null);
291 if ( itemName != null ) {
292 if ( addType.equals("AaiInternal") ) {
293 logger.debug( "addType AaiInternal, skip properties");
295 } else if ( getItemName == null) {
297 sbProperties.append(" " + getXmlRootElementName(addType) + ":\n");
298 sbProperties.append(" type: array\n items:\n");
299 sbProperties.append(" $ref: \"#/definitions/" + (itemName == "" ? "aai-internal" : itemName) + "\"\n");
300 if ( StringUtils.isNotEmpty(elementDescription) )
301 sbProperties.append(" description: " + elementDescription + "\n");
304 if ( ("java.util.ArrayList").equals(xmlElementElement.getAttribute("container-type"))) {
305 // need properties for getXmlRootElementName(addType)
306 namespaceFilter.add(getXmlRootElementName(addType));
307 if(getXmlRootElementName(addType).equals("service-capabilities"))
309 logger.info("arrays: "+ getXmlRootElementName(addType));
311 // newPathParams = new StringBuffer((pathParams == null ? "" : pathParams.toString())+sbParameters.toString()); //cp8128 - change this to not append pathParameters. Just use sbParameters
312 newPathParams = new StringBuffer(sbParameters.toString());
313 processJavaTypeElementSwagger( addType, getJavaTypeElementSwagger(addType),
314 pathSb, definitionsSb, path, tag == null ? useTag : tag, useOpId,
315 null, newPathParams, validEdges);
316 sbProperties.append(" " + getXmlRootElementName(addType) + ":\n");
317 sbProperties.append(" type: array\n items: \n");
318 sbProperties.append(" $ref: \"#/definitions/" + getXmlRootElementName(addType) + "\"\n");
319 if ( StringUtils.isNotEmpty(elementDescription) )
320 sbProperties.append(" description: " + elementDescription + "\n");
323 //Make sure certain types added to the filter don't appear
324 if (nodeFilter.contains(getXmlRootElementName(addType))) {
327 sbProperties.append(" " + getXmlRootElementName(addType) + ":\n");
328 sbProperties.append(" type: object\n");
329 sbProperties.append(" $ref: \"#/definitions/" + getXmlRootElementName(addType) + "\"\n");
332 if ( StringUtils.isNotEmpty(elementDescription) )
333 sbProperties.append(" description: " + elementDescription + "\n");
339 if ( sbParameters.toString().length() > 0 ) {
340 if ( pathParams == null )
341 pathParams = new StringBuffer();
342 pathParams.append(sbParameters);
344 if (indexedProps.isEmpty() && containerProps.isEmpty()){
345 NodeGetOperation get = new NodeGetOperation(useOpId, xmlRootElementName, tag, path, null);
346 String operation = get.toString();
347 if(StringUtils.isNotEmpty(operation)) {
348 operationDefinitions.put(xmlRootElementName, operation);
351 NodeGetOperation get = new NodeGetOperation(useOpId, xmlRootElementName, tag, path, pathParams == null ? "" : pathParams.toString());
352 String operation = get.toString();
353 if(StringUtils.isNotEmpty(operation)) {
354 operationDefinitions.put(xmlRootElementName, operation);
357 logger.debug("opId vs useOpId:"+opId+" vs "+useOpId+" PathParams="+pathParams);
359 if ( generatedJavaType.containsKey(xmlRootElementName) ) {
360 logger.debug("xmlRootElementName(1)="+xmlRootElementName);
363 boolean processingInventoryDef = false;
364 if ( xmlRootElementName.equals("inventory")) {
365 // inventory properties for each oxm to be concatenated
366 processingInventoryDef = true;
367 if ( inventoryDefSb == null ) {
368 inventoryDefSb = new StringBuilder();
369 definitionsSb.append(" " + xmlRootElementName + ":\n");
370 definitionsLocalSb.append(" " + xmlRootElementName + ":\n");
371 definitionsLocalSb.append(" properties:\n");
375 definitionsSb.append(" " + xmlRootElementName + ":\n");
376 definitionsLocalSb.append(" " + xmlRootElementName + ":\n");
378 DeleteFootnoteSet footnotes = new DeleteFootnoteSet(xmlRootElementName);
379 StringBuffer sbEdge = new StringBuffer();
380 LinkedHashSet<String> preventDelete = new LinkedHashSet<String>();
382 String nodeCaption = new String(" ###### Related Nodes\n");
384 EdgeRuleQuery q = new EdgeRuleQuery.Builder(xmlRootElementName).version(v).fromOnly().build();
385 Multimap<String, EdgeRule> results = ei.getRules(q);
386 SortedSet<String> ss=new TreeSet<String>(results.keySet());
387 sbEdge.append(nodeCaption);
389 for(String key : ss) {
390 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())));} );
391 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);} );
392 results.get(key).stream().filter((i) -> (i.getFrom().equals(xmlRootElementName) && (! i.isPrivateEdge() && i.getPreventDelete().equals("OUT")))).forEach((i) ->{ preventDelete.add(i.getTo().toUpperCase());} );
394 } catch(Exception e) {
395 logger.debug("xmlRootElementName: "+xmlRootElementName+"\n"+e);
398 EdgeRuleQuery q1 = new EdgeRuleQuery.Builder(xmlRootElementName).version(v).toOnly().build();
399 Multimap<String, EdgeRule> results = ei.getRules(q1);
400 SortedSet<String> ss=new TreeSet<String>(results.keySet());
401 sbEdge.append(nodeCaption);
402 for(String key : ss) {
403 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);} );
404 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())));} );
405 results.get(key).stream().filter((i) -> (i.getTo().equals(xmlRootElementName) && (! i.isPrivateEdge() && i.getPreventDelete().equals("IN")))).forEach((i) ->{ preventDelete.add(i.getFrom().toUpperCase());} );
407 } catch(Exception e) {
408 logger.debug("xmlRootElementName: "+xmlRootElementName+"\n"+e);
410 if(preventDelete.size() > 0) {
411 prevent = xmlRootElementName.toUpperCase()+" cannot be deleted if related to "+String.join(",",preventDelete);
412 logger.debug(prevent);
415 if(StringUtils.isNotEmpty(prevent)) {
416 footnotes.add(prevent);
418 if(footnotes.footnotes.size() > 0) {
419 sbEdge.append(footnotes.toString());
421 validEdges = sbEdge.toString();
423 // Handle description property. Might have a description OR valid edges OR both OR neither.
424 // Only put a description: tag if there is at least one.
425 if (StringUtils.isNotEmpty(pathDescriptionProperty) || StringUtils.isNotEmpty(validEdges) ) {
426 definitionsSb.append(" description: |\n");
427 definitionsLocalSb.append(" description: |\n");
429 if ( pathDescriptionProperty != null ) {
430 definitionsSb.append(" " + pathDescriptionProperty + "\n" );
431 definitionsLocalSb.append(" " + pathDescriptionProperty + "\n" );
433 definitionsSb.append(validEdges);
434 definitionsLocalSb.append(validEdges);
437 if ( requiredCnt > 0 ) {
438 definitionsSb.append(sbRequired);
439 definitionsLocalSb.append(sbRequired);
442 if ( propertyCnt > 0 ) {
443 definitionsSb.append(" properties:\n");
444 definitionsSb.append(sbProperties);
445 if ( !processingInventoryDef) {
446 definitionsLocalSb.append(" properties:\n");
448 definitionsLocalSb.append(sbProperties);
451 namespaceFilter.add(xmlRootElementName);
452 if ( xmlRootElementName.equals("inventory") ) {
453 //will add to javaTypeDefinitions at end
454 inventoryDefSb.append(definitionsLocalSb.toString());
456 javaTypeDefinitions.put(xmlRootElementName, definitionsLocalSb.toString());
458 } catch (Exception e) {
461 if ( xmlRootElementName.equals("inventory") ) {
462 logger.trace("skip xmlRootElementName(2)="+xmlRootElementName);
465 generatedJavaType.put(xmlRootElementName, null);
466 //Write operations by Namespace(tagName)
468 if( validTag(javaTypeName) && javaTypeName == useTag && tag == null) {
469 writeYAMLfile("nodes_"+javaTypeName, getDocumentHeader()+pathSb.toString()+appendDefinitions(namespaceFilter));
470 totalPathSbAccumulator.append(pathSb);
471 pathSb.delete(0, pathSb.length());
472 namespaceFilter.clear();
475 logger.debug("xmlRootElementName(2)="+xmlRootElementName);
479 private void writeYAMLfile(String outfileName, String fileContent) {
480 outfileName = (StringUtils.isEmpty(outfileName)) ? "aai_swagger" : outfileName;
481 outfileName = (outfileName.lastIndexOf(File.separator) == -1) ? yaml_dir + File.separator +outfileName+"_" + v.toString() + "." + generateTypeYAML : outfileName;
482 File outfile = new File(outfileName);
483 File parentDir = outfile.getParentFile();
484 if(parentDir != null && ! parentDir.exists())
487 outfile.createNewFile();
488 } catch (IOException e) {
489 logger.error( "Exception creating output file " + outfileName);
492 BufferedWriter bw = null;
494 Charset charset = Charset.forName("UTF-8");
495 Path path = Paths.get(outfileName);
496 bw = Files.newBufferedWriter(path, charset);
497 bw.write(fileContent);
501 } catch ( IOException e) {
502 logger.error( "Exception writing output file " + outfileName);
507 public boolean validTag(String tag) {
513 case "ServiceDesignAndCreation":
515 case "LicenseManagement":
516 case "CloudInfrastructure":
523 public String appendOperations() {
525 StringBuffer sb = new StringBuffer();
526 Map<String, String> sortedOperationDefinitions = new TreeMap<String, String>(operationDefinitions);
527 for (Map.Entry<String, String> entry : sortedOperationDefinitions.entrySet()) {
528 sb.append(entry.getValue());
530 return sb.toString();