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.schemagen.genxsd;
22 import com.google.common.collect.Multimap;
23 import org.apache.commons.lang3.StringUtils;
24 import org.onap.aai.edges.EdgeIngestor;
25 import org.onap.aai.edges.EdgeRule;
26 import org.onap.aai.edges.EdgeRuleQuery;
27 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
28 import org.onap.aai.nodes.NodeIngestor;
29 import org.onap.aai.setup.SchemaVersion;
30 import org.onap.aai.setup.SchemaVersions;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33 import org.w3c.dom.Element;
34 import org.w3c.dom.NodeList;
35 import org.xml.sax.SAXException;
37 import javax.xml.parsers.ParserConfigurationException;
38 import java.io.BufferedWriter;
40 import java.io.FileNotFoundException;
41 import java.io.IOException;
42 import java.nio.charset.Charset;
43 import java.nio.file.Files;
44 import java.nio.file.Path;
45 import java.nio.file.Paths;
48 public class NodesYAMLfromOXM extends OxmFileProcessor {
49 private static final Logger logger = LoggerFactory.getLogger("GenerateXsd.class");
50 private static final String root = "../aai-schema/src/main/resources";
51 private static final String autoGenRoot = "aai-schema/src/main/resources";
52 private static final String generateTypeYAML = "yaml";
53 private static final String normalStartDir = "aai-schema-gen";
54 private static final String yaml_dir = (((System.getProperty("user.dir") != null) && (!System.getProperty("user.dir").contains(normalStartDir))) ? autoGenRoot : root) + "/aai_swagger_yaml";
55 private StringBuilder inventoryDefSb = null;
56 private Map<String,String> operationDefinitions = new HashMap<>();
58 private String basePath;
60 public NodesYAMLfromOXM(String basePath, SchemaVersions schemaVersions, NodeIngestor ni, EdgeIngestor ei){
61 super(schemaVersions, ni,ei);
62 this.basePath = basePath;
64 public void setOxmVersion(File oxmFile, SchemaVersion v) {
65 super.setOxmVersion(oxmFile, v);
67 public void setXmlVersion(String xml, SchemaVersion v){
68 super.setXmlVersion(xml, v);
71 public void setVersion(SchemaVersion v) {
76 public String getDocumentHeader() {
77 StringBuffer sb = new StringBuffer();
78 sb.append("swagger: \"2.0\"\ninfo:\n ");
79 sb.append("description: |");
80 if ( versionSupportsSwaggerDiff(v.toString())) {
81 sb.append("\n\n [Differences versus the previous schema version]("+"apidocs/aai_swagger_" + v.toString() + ".diff)");
83 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");
84 sb.append(" title: Active and Available Inventory REST API\n");
85 sb.append(" license:\n name: Apache 2.0\n url: http://www.apache.org/licenses/LICENSE-2.0.html\n");
86 sb.append(" contact:\n name:\n url:\n email:\n");
87 sb.append("host:\nbasePath: " + basePath + "/" + v.toString() + "\n");
88 sb.append("schemes:\n - https\npaths:\n");
92 protected void init() throws ParserConfigurationException, SAXException, IOException, FileNotFoundException, EdgeRuleNotFoundException {
97 public String process() throws ParserConfigurationException, SAXException, IOException, FileNotFoundException, EdgeRuleNotFoundException {
98 StringBuffer sb = new StringBuffer();
99 StringBuffer pathSb = new StringBuffer();
100 NodeGetOperation.resetContainers();
103 } catch(Exception e) {
104 logger.error( "Error initializing " + this.getClass());
107 pathSb.append(getDocumentHeader());
108 StringBuffer definitionsSb = new StringBuffer();
111 for ( int i = 0; i < javaTypeNodes.getLength(); ++ i ) {
112 elem = (Element)javaTypeNodes.item(i);
113 javaTypeName = elem.getAttribute("name");
114 if ( !"Inventory".equals(javaTypeName ) ) {
115 if ( generatedJavaType.containsKey(javaTypeName) ) {
118 // will combine all matching java-types
119 elem = getJavaTypeElementSwagger(javaTypeName );
122 XSDElement javaTypeElement = new XSDElement(elem);
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 SAXException(msg);
130 namespaceFilter.add(getXmlRootElementName(javaTypeName));
131 processJavaTypeElementSwagger( javaTypeName, javaTypeElement, pathSb,
132 definitionsSb, null, null, null, null, null, null);
135 // sb.append(getDocumentHeader());
136 // sb.append(totalPathSbAccumulator);
137 sb.append(appendOperations());
138 sb.append(appendDefinitions());
139 PutRelationPathSet prp = new PutRelationPathSet(v);
140 prp.generateRelations(ei);
141 return sb.toString();
144 public String appendDefinitions() {
145 return appendDefinitions(null);
148 public String appendDefinitions(Set<String> namespaceFilter) {
149 if ( inventoryDefSb != null ) {
150 javaTypeDefinitions.put("inventory", inventoryDefSb.toString());
152 StringBuffer sb = new StringBuffer("definitions:\n");
153 Map<String, String> sortedJavaTypeDefinitions = new TreeMap<>(javaTypeDefinitions);
155 for (Map.Entry<String, String> entry : sortedJavaTypeDefinitions.entrySet()) {
156 if(namespaceFilter != null && (! namespaceFilter.contains(entry.getKey()))) {
159 sb.append(entry.getValue());
161 return sb.toString();
164 private String processJavaTypeElementSwagger( String javaTypeName, Element javaTypeElement,
165 StringBuffer pathSb, StringBuffer definitionsSb, String path, String tag, String opId,
166 String getItemName, StringBuffer pathParams, String validEdges) {
168 String xmlRootElementName = getXMLRootElementName(javaTypeElement);
169 StringBuilder definitionsLocalSb = new StringBuilder(256);
171 String useTag = null;
172 String useOpId = null;
173 logger.debug("tag="+tag);
175 if(tag != null && (! validTag(tag))) {
176 logger.debug("tag="+tag+"; javaTypeName="+javaTypeName);
179 if ( !javaTypeName.equals("Inventory") ) {
180 if ( javaTypeName.equals("AaiInternal"))
183 useOpId = javaTypeName;
185 useOpId = opId + javaTypeName;
187 useTag = javaTypeName;
190 path = xmlRootElementName.equals("inventory") ? "" : (path == null) ? "/" + xmlRootElementName : path + "/" + xmlRootElementName;
191 XSDJavaType javaType = new XSDJavaType(javaTypeElement);
192 if ( getItemName != null) {
193 if ( getItemName.equals("array") )
194 return javaType.getArrayType();
196 return javaType.getItemName();
199 NodeList parentNodes = javaTypeElement.getElementsByTagName("java-attributes");
200 if ( parentNodes.getLength() == 0 ) {
201 logger.debug( "no java-attributes for java-type " + javaTypeName);
205 String pathDescriptionProperty = javaType.getPathDescriptionProperty();
206 String container = javaType.getContainerProperty();
207 Vector<String> indexedProps = javaType.getIndexedProps();
208 Vector<String> containerProps = new Vector<String>();
209 if(container != null) {
210 logger.debug("javaTypeName " + javaTypeName + " container:" + container +" indexedProps:"+indexedProps);
213 Element parentElement = (Element)parentNodes.item(0);
214 NodeList xmlElementNodes = parentElement.getElementsByTagName("xml-element");
216 StringBuffer sbParameters = new StringBuffer();
217 StringBuffer sbRequired = new StringBuffer();
220 StringBuffer sbProperties = new StringBuffer();
222 if ( appliedPaths.containsKey(path))
225 StringTokenizer st = new StringTokenizer(path, "/");
226 logger.debug("path: " + path + " st? " + st.toString());
227 if ( st.countTokens() > 1 && getItemName == null ) {
228 logger.debug("appliedPaths: " + appliedPaths + " containsKey? " + appliedPaths.containsKey(path));
229 appliedPaths.put(path, xmlRootElementName);
231 Vector<String> addTypeV = null;
232 for ( int i = 0; i < xmlElementNodes.getLength(); ++i ) {
233 XSDElement xmlElementElement = new XSDElement((Element)xmlElementNodes.item(i));
234 if ( !xmlElementElement.getParentNode().isSameNode(parentElement))
236 String elementDescription=xmlElementElement.getPathDescriptionProperty();
237 if(getItemName == null) {
238 addTypeV = xmlElementElement.getAddTypes(v.toString());
240 if ( "true".equals(xmlElementElement.getAttribute("xml-key"))) {
241 path += "/{" + xmlElementElement.getAttribute("name") + "}";
243 logger.debug("path: " + path);
244 logger.debug( "xmlElementElement.getAttribute(required):"+xmlElementElement.getAttribute("required") );
246 if ( ("true").equals(xmlElementElement.getAttribute("required"))) {
247 if ( requiredCnt == 0 )
248 sbRequired.append(" required:\n");
250 if ( addTypeV == null || addTypeV.isEmpty()) {
251 sbRequired.append(" - " + xmlElementElement.getAttribute("name") + "\n");
253 for ( int k = 0; k < addTypeV.size(); ++k ) {
254 sbRequired.append(" - " + getXmlRootElementName(addTypeV.elementAt(k)) + ":\n");
259 if ( "true".equals(xmlElementElement.getAttribute("xml-key")) ) {
260 sbParameters.append(xmlElementElement.getPathParamYAML(elementDescription));
262 if ( indexedProps != null
263 && indexedProps.contains(xmlElementElement.getAttribute("name") ) ) {
264 containerProps.add(xmlElementElement.getQueryParamYAML());
265 NodeGetOperation.addContainerProps(container, containerProps);
267 if ( xmlElementElement.isStandardType()) {
268 sbProperties.append(xmlElementElement.getTypePropertyYAML());
272 // 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
273 StringBuffer newPathParams = new StringBuffer(sbParameters.toString());
274 for ( int k = 0; addTypeV != null && k < addTypeV.size(); ++k ) {
275 String addType = addTypeV.elementAt(k);
276 namespaceFilter.add(getXmlRootElementName(addType));
277 if ( opId == null || !opId.contains(addType)) {
278 processJavaTypeElementSwagger( addType, getJavaTypeElementSwagger(addType),
279 pathSb, definitionsSb, path, tag == null ? useTag : tag, useOpId, null,
280 newPathParams, validEdges);
282 // need item name of array
283 String itemName = processJavaTypeElementSwagger( addType, getJavaTypeElementSwagger(addType),
284 pathSb, definitionsSb, path, tag == null ? useTag : tag, useOpId,
285 "array", null, null);
287 if ( itemName != null ) {
288 if ( addType.equals("AaiInternal") ) {
289 logger.debug( "addType AaiInternal, skip properties");
291 } else if ( getItemName == null) {
293 sbProperties.append(" " + getXmlRootElementName(addType) + ":\n");
294 sbProperties.append(" type: array\n items:\n");
295 sbProperties.append(" $ref: \"#/definitions/" + (itemName == "" ? "aai-internal" : itemName) + "\"\n");
296 if ( StringUtils.isNotEmpty(elementDescription) )
297 sbProperties.append(" description: " + elementDescription + "\n");
300 if ( ("java.util.ArrayList").equals(xmlElementElement.getAttribute("container-type"))) {
301 // need properties for getXmlRootElementName(addType)
302 namespaceFilter.add(getXmlRootElementName(addType));
303 if(getXmlRootElementName(addType).equals("service-capabilities"))
305 logger.info("arrays: "+ getXmlRootElementName(addType));
307 // newPathParams = new StringBuffer((pathParams == null ? "" : pathParams.toString())+sbParameters.toString()); //cp8128 - change this to not append pathParameters. Just use sbParameters
308 newPathParams = new StringBuffer(sbParameters.toString());
309 processJavaTypeElementSwagger( addType, getJavaTypeElementSwagger(addType),
310 pathSb, definitionsSb, path, tag == null ? useTag : tag, useOpId,
311 null, newPathParams, validEdges);
312 sbProperties.append(" " + getXmlRootElementName(addType) + ":\n");
313 sbProperties.append(" type: array\n items: \n");
314 sbProperties.append(" $ref: \"#/definitions/" + getXmlRootElementName(addType) + "\"\n");
315 if ( StringUtils.isNotEmpty(elementDescription) )
316 sbProperties.append(" description: " + elementDescription + "\n");
319 //Make sure certain types added to the filter don't appear
320 if (nodeFilter.contains(getXmlRootElementName(addType))) {
323 sbProperties.append(" " + getXmlRootElementName(addType) + ":\n");
324 sbProperties.append(" type: object\n");
325 sbProperties.append(" $ref: \"#/definitions/" + getXmlRootElementName(addType) + "\"\n");
328 if ( StringUtils.isNotEmpty(elementDescription) )
329 sbProperties.append(" description: " + elementDescription + "\n");
335 if ( sbParameters.toString().length() > 0 ) {
336 if ( pathParams == null )
337 pathParams = new StringBuffer();
338 pathParams.append(sbParameters);
340 if (indexedProps.isEmpty() && containerProps.isEmpty()){
341 NodeGetOperation get = new NodeGetOperation(useOpId, xmlRootElementName, tag, path, null);
342 String operation = get.toString();
343 if(StringUtils.isNotEmpty(operation)) {
344 operationDefinitions.put(xmlRootElementName, operation);
347 NodeGetOperation get = new NodeGetOperation(useOpId, xmlRootElementName, tag, path, pathParams == null ? "" : pathParams.toString());
348 String operation = get.toString();
349 if(StringUtils.isNotEmpty(operation)) {
350 operationDefinitions.put(xmlRootElementName, operation);
353 logger.debug("opId vs useOpId:"+opId+" vs "+useOpId+" PathParams="+pathParams);
355 if ( generatedJavaType.containsKey(xmlRootElementName) ) {
356 logger.debug("xmlRootElementName(1)="+xmlRootElementName);
359 boolean processingInventoryDef = false;
360 if ( xmlRootElementName.equals("inventory")) {
361 // inventory properties for each oxm to be concatenated
362 processingInventoryDef = true;
363 if ( inventoryDefSb == null ) {
364 inventoryDefSb = new StringBuilder();
365 definitionsSb.append(" " + xmlRootElementName + ":\n");
366 definitionsLocalSb.append(" " + xmlRootElementName + ":\n");
367 definitionsLocalSb.append(" properties:\n");
371 definitionsSb.append(" " + xmlRootElementName + ":\n");
372 definitionsLocalSb.append(" " + xmlRootElementName + ":\n");
374 DeleteFootnoteSet footnotes = new DeleteFootnoteSet(xmlRootElementName);
375 StringBuffer sbEdge = new StringBuffer();
376 LinkedHashSet<String> preventDelete = new LinkedHashSet<String>();
378 String nodeCaption = new String(" ###### Related Nodes\n");
380 EdgeRuleQuery q = new EdgeRuleQuery.Builder(xmlRootElementName).version(v).fromOnly().build();
381 Multimap<String, EdgeRule> results = ei.getRules(q);
382 SortedSet<String> ss=new TreeSet<>(results.keySet());
383 sbEdge.append(nodeCaption);
385 for(String key : ss) {
386 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())));} );
387 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);} );
388 results.get(key).stream().filter((i) -> (i.getFrom().equals(xmlRootElementName) && (! i.isPrivateEdge() && i.getPreventDelete().equals("OUT")))).forEach((i) ->{ preventDelete.add(i.getTo().toUpperCase());} );
390 } catch(Exception e) {
391 logger.debug("xmlRootElementName: "+xmlRootElementName+"\n"+e);
394 EdgeRuleQuery q1 = new EdgeRuleQuery.Builder(xmlRootElementName).version(v).toOnly().build();
395 Multimap<String, EdgeRule> results = ei.getRules(q1);
396 SortedSet<String> ss=new TreeSet<String>(results.keySet());
397 sbEdge.append(nodeCaption);
398 for(String key : ss) {
399 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);} );
400 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())));} );
401 results.get(key).stream().filter((i) -> (i.getTo().equals(xmlRootElementName) && (! i.isPrivateEdge() && i.getPreventDelete().equals("IN")))).forEach((i) ->{ preventDelete.add(i.getFrom().toUpperCase());} );
403 } catch(Exception e) {
404 logger.debug("xmlRootElementName: "+xmlRootElementName+"\n"+e);
406 if(preventDelete.size() > 0) {
407 prevent = xmlRootElementName.toUpperCase()+" cannot be deleted if related to "+String.join(",",preventDelete);
408 logger.debug(prevent);
411 if(StringUtils.isNotEmpty(prevent)) {
412 footnotes.add(prevent);
414 if(footnotes.footnotes.size() > 0) {
415 sbEdge.append(footnotes.toString());
417 validEdges = sbEdge.toString();
419 // Handle description property. Might have a description OR valid edges OR both OR neither.
420 // Only put a description: tag if there is at least one.
421 if (StringUtils.isNotEmpty(pathDescriptionProperty) || StringUtils.isNotEmpty(validEdges) ) {
422 definitionsSb.append(" description: |\n");
423 definitionsLocalSb.append(" description: |\n");
425 if ( pathDescriptionProperty != null ) {
426 definitionsSb.append(" " + pathDescriptionProperty + "\n" );
427 definitionsLocalSb.append(" " + pathDescriptionProperty + "\n" );
429 definitionsSb.append(validEdges);
430 definitionsLocalSb.append(validEdges);
433 if ( requiredCnt > 0 ) {
434 definitionsSb.append(sbRequired);
435 definitionsLocalSb.append(sbRequired);
438 if ( propertyCnt > 0 ) {
439 definitionsSb.append(" properties:\n");
440 definitionsSb.append(sbProperties);
441 if ( !processingInventoryDef) {
442 definitionsLocalSb.append(" properties:\n");
444 definitionsLocalSb.append(sbProperties);
447 namespaceFilter.add(xmlRootElementName);
448 if ( xmlRootElementName.equals("inventory") ) {
449 //will add to javaTypeDefinitions at end
450 inventoryDefSb.append(definitionsLocalSb.toString());
452 javaTypeDefinitions.put(xmlRootElementName, definitionsLocalSb.toString());
454 } catch (Exception e) {
457 if ( xmlRootElementName.equals("inventory") ) {
458 logger.trace("skip xmlRootElementName(2)="+xmlRootElementName);
461 generatedJavaType.put(xmlRootElementName, null);
462 //Write operations by Namespace(tagName)
464 if( validTag(javaTypeName) && javaTypeName == useTag && tag == null) {
465 writeYAMLfile("nodes_"+javaTypeName, getDocumentHeader()+pathSb.toString()+appendDefinitions(namespaceFilter));
466 totalPathSbAccumulator.append(pathSb);
467 pathSb.delete(0, pathSb.length());
468 namespaceFilter.clear();
471 logger.debug("xmlRootElementName(2)="+xmlRootElementName);
475 private void writeYAMLfile(String outfileName, String fileContent) {
476 outfileName = (StringUtils.isEmpty(outfileName)) ? "aai_swagger" : outfileName;
477 outfileName = (outfileName.lastIndexOf(File.separator) == -1) ? yaml_dir + File.separator +outfileName+"_" + v.toString() + "." + generateTypeYAML : outfileName;
478 File outfile = new File(outfileName);
479 File parentDir = outfile.getParentFile();
480 if(parentDir != null && ! parentDir.exists())
483 outfile.createNewFile();
484 } catch (IOException e) {
485 logger.error( "Exception creating output file " + outfileName);
488 Path path = Paths.get(outfileName);
489 Charset charset = Charset.forName("UTF-8");
490 try(BufferedWriter bw = Files.newBufferedWriter(path, charset);) {
491 bw.write(fileContent);
495 } catch ( IOException e) {
496 logger.error( "Exception writing output file " + outfileName);
501 public boolean validTag(String tag) {
507 case "ServiceDesignAndCreation":
509 case "LicenseManagement":
510 case "CloudInfrastructure":
517 public String appendOperations() {
519 StringBuffer sb = new StringBuffer();
520 Map<String, String> sortedOperationDefinitions = new TreeMap<String, String>(operationDefinitions);
521 for (Map.Entry<String, String> entry : sortedOperationDefinitions.entrySet()) {
522 sb.append(entry.getValue());
524 return sb.toString();