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);
136 logger.debug("External: "+javaTypeElement.getAttribute("name")+"/"+getXmlRootElementName(javaTypeName));
137 if ( javaTypeName == null ) {
138 String msg = "Invalid OXM file: <java-type> has no name attribute in " + oxmFile;
140 throw new AAIException(msg);
142 namespaceFilter.add(getXmlRootElementName(javaTypeName));
144 processJavaTypeElementSwagger( javaTypeName, javaTypeElement, pathSb,
145 definitionsSb, null, null, null, null, null, null);
148 // sb.append(getDocumentHeader());
149 // sb.append(totalPathSbAccumulator);
150 sb.append(appendOperations());
151 sb.append(appendDefinitions());
152 PutRelationPathSet prp = new PutRelationPathSet(v);
153 prp.generateRelations(ei);
154 return sb.toString();
157 public String appendDefinitions() {
158 return appendDefinitions(null);
161 public String appendDefinitions(Set<String> namespaceFilter) {
162 if ( inventoryDefSb != null ) {
163 javaTypeDefinitions.put("inventory", inventoryDefSb.toString());
165 StringBuffer sb = new StringBuffer("definitions:\n");
166 Map<String, String> sortedJavaTypeDefinitions = new TreeMap<>(javaTypeDefinitions);
168 for (Map.Entry<String, String> entry : sortedJavaTypeDefinitions.entrySet()) {
169 if(namespaceFilter != null && (! namespaceFilter.contains(entry.getKey()))) {
172 sb.append(entry.getValue());
174 return sb.toString();
177 private String processJavaTypeElementSwagger( String javaTypeName, Element javaTypeElement,
178 StringBuffer pathSb, StringBuffer definitionsSb, String path, String tag, String opId,
179 String getItemName, StringBuffer pathParams, String validEdges) {
181 String xmlRootElementName = getXMLRootElementName(javaTypeElement);
182 StringBuilder definitionsLocalSb = new StringBuilder(256);
184 String useTag = null;
185 String useOpId = null;
186 logger.debug("tag="+tag);
188 if(tag != null && (! validTag(tag))) {
189 logger.debug("tag="+tag+"; javaTypeName="+javaTypeName);
192 if ( !javaTypeName.equals("Inventory") ) {
193 if ( javaTypeName.equals("AaiInternal"))
196 useOpId = javaTypeName;
198 useOpId = opId + javaTypeName;
200 useTag = javaTypeName;
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();
209 return javaType.getItemName();
212 NodeList parentNodes = javaTypeElement.getElementsByTagName("java-attributes");
213 if ( parentNodes.getLength() == 0 ) {
214 logger.debug( "no java-attributes for java-type " + javaTypeName);
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);
226 Element parentElement = (Element)parentNodes.item(0);
227 NodeList xmlElementNodes = parentElement.getElementsByTagName("xml-element");
229 StringBuffer sbParameters = new StringBuffer();
230 StringBuffer sbRequired = new StringBuffer();
233 StringBuffer sbProperties = new StringBuffer();
235 if ( appliedPaths.containsKey(path))
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);
244 Vector<String> addTypeV = null;
245 for ( int i = 0; i < xmlElementNodes.getLength(); ++i ) {
246 XSDElement xmlElementElement = new XSDElement((Element)xmlElementNodes.item(i));
247 if ( !xmlElementElement.getParentNode().isSameNode(parentElement))
249 String elementDescription=xmlElementElement.getPathDescriptionProperty();
250 if(getItemName == null) {
251 addTypeV = xmlElementElement.getAddTypes(v.toString());
253 if ( "true".equals(xmlElementElement.getAttribute("xml-key"))) {
254 path += "/{" + xmlElementElement.getAttribute("name") + "}";
256 logger.debug("path: " + path);
257 logger.debug( "xmlElementElement.getAttribute(required):"+xmlElementElement.getAttribute("required") );
259 if ( ("true").equals(xmlElementElement.getAttribute("required"))) {
260 if ( requiredCnt == 0 )
261 sbRequired.append(" required:\n");
263 if ( addTypeV == null || addTypeV.isEmpty()) {
264 sbRequired.append(" - " + xmlElementElement.getAttribute("name") + "\n");
266 for ( int k = 0; k < addTypeV.size(); ++k ) {
267 sbRequired.append(" - " + getXmlRootElementName(addTypeV.elementAt(k)) + ":\n");
272 if ( "true".equals(xmlElementElement.getAttribute("xml-key")) ) {
273 sbParameters.append(xmlElementElement.getPathParamYAML(elementDescription));
275 if ( indexedProps != null
276 && indexedProps.contains(xmlElementElement.getAttribute("name") ) ) {
277 containerProps.add(xmlElementElement.getQueryParamYAML());
278 NodeGetOperation.addContainerProps(container, containerProps);
280 if ( xmlElementElement.isStandardType()) {
281 sbProperties.append(xmlElementElement.getTypePropertyYAML());
285 // 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
286 StringBuffer newPathParams = new StringBuffer(sbParameters.toString());
287 for ( int k = 0; addTypeV != null && k < addTypeV.size(); ++k ) {
288 String addType = addTypeV.elementAt(k);
289 namespaceFilter.add(getXmlRootElementName(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);
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);
300 if ( itemName != null ) {
301 if ( addType.equals("AaiInternal") ) {
302 logger.debug( "addType AaiInternal, skip properties");
304 } else if ( getItemName == null) {
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");
313 if ( ("java.util.ArrayList").equals(xmlElementElement.getAttribute("container-type"))) {
314 // need properties for getXmlRootElementName(addType)
315 namespaceFilter.add(getXmlRootElementName(addType));
316 if(getXmlRootElementName(addType).equals("service-capabilities"))
318 logger.info("arrays: "+ getXmlRootElementName(addType));
320 // newPathParams = new StringBuffer((pathParams == null ? "" : pathParams.toString())+sbParameters.toString()); //cp8128 - change this to not append pathParameters. Just use sbParameters
321 newPathParams = new StringBuffer(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");
332 //Make sure certain types added to the filter don't appear
333 if (nodeFilter.contains(getXmlRootElementName(addType))) {
336 sbProperties.append(" " + getXmlRootElementName(addType) + ":\n");
337 sbProperties.append(" type: object\n");
338 sbProperties.append(" $ref: \"#/definitions/" + getXmlRootElementName(addType) + "\"\n");
341 if ( StringUtils.isNotEmpty(elementDescription) )
342 sbProperties.append(" description: " + elementDescription + "\n");
348 if ( sbParameters.toString().length() > 0 ) {
349 if ( pathParams == null )
350 pathParams = new StringBuffer();
351 pathParams.append(sbParameters);
353 if (indexedProps.isEmpty() && containerProps.isEmpty()){
354 NodeGetOperation get = new NodeGetOperation(useOpId, xmlRootElementName, tag, path, null);
355 String operation = get.toString();
356 if(StringUtils.isNotEmpty(operation)) {
357 operationDefinitions.put(xmlRootElementName, operation);
360 NodeGetOperation get = new NodeGetOperation(useOpId, xmlRootElementName, tag, path, pathParams == null ? "" : pathParams.toString());
361 String operation = get.toString();
362 if(StringUtils.isNotEmpty(operation)) {
363 operationDefinitions.put(xmlRootElementName, operation);
366 logger.debug("opId vs useOpId:"+opId+" vs "+useOpId+" PathParams="+pathParams);
368 if ( generatedJavaType.containsKey(xmlRootElementName) ) {
369 logger.debug("xmlRootElementName(1)="+xmlRootElementName);
372 boolean processingInventoryDef = false;
373 if ( xmlRootElementName.equals("inventory")) {
374 // inventory properties for each oxm to be concatenated
375 processingInventoryDef = true;
376 if ( inventoryDefSb == null ) {
377 inventoryDefSb = new StringBuilder();
378 definitionsSb.append(" " + xmlRootElementName + ":\n");
379 definitionsLocalSb.append(" " + xmlRootElementName + ":\n");
380 definitionsLocalSb.append(" properties:\n");
384 definitionsSb.append(" " + xmlRootElementName + ":\n");
385 definitionsLocalSb.append(" " + xmlRootElementName + ":\n");
387 DeleteFootnoteSet footnotes = new DeleteFootnoteSet(xmlRootElementName);
388 StringBuffer sbEdge = new StringBuffer();
389 LinkedHashSet<String> preventDelete = new LinkedHashSet<String>();
391 String nodeCaption = new String(" ###### Related Nodes\n");
393 EdgeRuleQuery q = new EdgeRuleQuery.Builder(xmlRootElementName).version(v).fromOnly().build();
394 Multimap<String, EdgeRule> results = ei.getRules(q);
395 SortedSet<String> ss=new TreeSet<>(results.keySet());
396 sbEdge.append(nodeCaption);
398 for(String key : ss) {
399 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())));} );
400 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);} );
401 results.get(key).stream().filter((i) -> (i.getFrom().equals(xmlRootElementName) && (! i.isPrivateEdge() && i.getPreventDelete().equals("OUT")))).forEach((i) ->{ preventDelete.add(i.getTo().toUpperCase());} );
403 } catch(Exception e) {
404 logger.debug("xmlRootElementName: "+xmlRootElementName+"\n"+e);
407 EdgeRuleQuery q1 = new EdgeRuleQuery.Builder(xmlRootElementName).version(v).toOnly().build();
408 Multimap<String, EdgeRule> results = ei.getRules(q1);
409 SortedSet<String> ss=new TreeSet<String>(results.keySet());
410 sbEdge.append(nodeCaption);
411 for(String key : ss) {
412 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);} );
413 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())));} );
414 results.get(key).stream().filter((i) -> (i.getTo().equals(xmlRootElementName) && (! i.isPrivateEdge() && i.getPreventDelete().equals("IN")))).forEach((i) ->{ preventDelete.add(i.getFrom().toUpperCase());} );
416 } catch(Exception e) {
417 logger.debug("xmlRootElementName: "+xmlRootElementName+"\n"+e);
419 if(preventDelete.size() > 0) {
420 prevent = xmlRootElementName.toUpperCase()+" cannot be deleted if related to "+String.join(",",preventDelete);
421 logger.debug(prevent);
424 if(StringUtils.isNotEmpty(prevent)) {
425 footnotes.add(prevent);
427 if(footnotes.footnotes.size() > 0) {
428 sbEdge.append(footnotes.toString());
430 validEdges = sbEdge.toString();
432 // Handle description property. Might have a description OR valid edges OR both OR neither.
433 // Only put a description: tag if there is at least one.
434 if (StringUtils.isNotEmpty(pathDescriptionProperty) || StringUtils.isNotEmpty(validEdges) ) {
435 definitionsSb.append(" description: |\n");
436 definitionsLocalSb.append(" description: |\n");
438 if ( pathDescriptionProperty != null ) {
439 definitionsSb.append(" " + pathDescriptionProperty + "\n" );
440 definitionsLocalSb.append(" " + pathDescriptionProperty + "\n" );
442 definitionsSb.append(validEdges);
443 definitionsLocalSb.append(validEdges);
446 if ( requiredCnt > 0 ) {
447 definitionsSb.append(sbRequired);
448 definitionsLocalSb.append(sbRequired);
451 if ( propertyCnt > 0 ) {
452 definitionsSb.append(" properties:\n");
453 definitionsSb.append(sbProperties);
454 if ( !processingInventoryDef) {
455 definitionsLocalSb.append(" properties:\n");
457 definitionsLocalSb.append(sbProperties);
460 namespaceFilter.add(xmlRootElementName);
461 if ( xmlRootElementName.equals("inventory") ) {
462 //will add to javaTypeDefinitions at end
463 inventoryDefSb.append(definitionsLocalSb.toString());
465 javaTypeDefinitions.put(xmlRootElementName, definitionsLocalSb.toString());
467 } catch (Exception e) {
470 if ( xmlRootElementName.equals("inventory") ) {
471 logger.trace("skip xmlRootElementName(2)="+xmlRootElementName);
474 generatedJavaType.put(xmlRootElementName, null);
475 //Write operations by Namespace(tagName)
477 if( validTag(javaTypeName) && javaTypeName == useTag && tag == null) {
478 writeYAMLfile("nodes_"+javaTypeName, getDocumentHeader()+pathSb.toString()+appendDefinitions(namespaceFilter));
479 totalPathSbAccumulator.append(pathSb);
480 pathSb.delete(0, pathSb.length());
481 namespaceFilter.clear();
484 logger.debug("xmlRootElementName(2)="+xmlRootElementName);
488 private void writeYAMLfile(String outfileName, String fileContent) {
489 outfileName = (StringUtils.isEmpty(outfileName)) ? "aai_swagger" : outfileName;
490 outfileName = (outfileName.lastIndexOf(File.separator) == -1) ? yaml_dir + File.separator +outfileName+"_" + v.toString() + "." + generateTypeYAML : outfileName;
491 File outfile = new File(outfileName);
492 File parentDir = outfile.getParentFile();
493 if(parentDir != null && ! parentDir.exists())
496 outfile.createNewFile();
497 } catch (IOException e) {
498 logger.error( "Exception creating output file " + outfileName);
501 Path path = Paths.get(outfileName);
502 Charset charset = Charset.forName("UTF-8");
504 BufferedWriter bw = Files.newBufferedWriter(path, charset);
505 bw.write(fileContent);
509 } catch ( IOException e) {
510 logger.error( "Exception writing output file " + outfileName);
515 public boolean validTag(String tag) {
521 case "ServiceDesignAndCreation":
523 case "LicenseManagement":
524 case "CloudInfrastructure":
531 public String appendOperations() {
533 StringBuffer sb = new StringBuffer();
534 Map<String, String> sortedOperationDefinitions = new TreeMap<String, String>(operationDefinitions);
535 for (Map.Entry<String, String> entry : sortedOperationDefinitions.entrySet()) {
536 sb.append(entry.getValue());
538 return sb.toString();