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 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;
31 import javax.xml.parsers.ParserConfigurationException;
32 import java.io.BufferedWriter;
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;
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";
50 private File edgeFile;
51 private EdgeRuleSet edgeRuleSet = null;
52 public YAMLfromOXM(File oxmFile, Version v, File edgeFile) throws ParserConfigurationException, SAXException, IOException, AAIException, FileNotFoundException {
54 this.edgeFile = edgeFile;
57 public YAMLfromOXM(String xml, Version v, File edgeFile) throws ParserConfigurationException, SAXException, IOException, AAIException, FileNotFoundException {
59 this.edgeFile = edgeFile;
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");
78 protected void init() throws ParserConfigurationException, SAXException, IOException, AAIException, FileNotFoundException {
80 edgeRuleSet = new EdgeRuleSet(edgeFile);
84 public String process() throws AAIException {
85 StringBuffer sb = new StringBuffer();
86 EdgeRuleSet edgeRuleSet = null;
88 edgeRuleSet = new EdgeRuleSet(edgeFile);
89 } catch (Exception e) {
90 logger.warn("No valid Edge Rule Set available("+edgeFile+"): "+e.getMessage());
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;
103 throw new AAIException(msg);
105 //Skip any type that has already been processed(recursion could be the reason)
106 if ( generatedJavaType.containsKey(getXmlRootElementName(javaTypeName)) ) {
109 processJavaTypeElementSwagger( javaTypeName, javaTypeElement, pathSb,
110 definitionsSb, null, null, null, null, null, null);
114 sb.append(appendDefinitions());
115 PutRelationPathSet prp = new PutRelationPathSet(v);
116 prp.generateRelations(edgeRuleSet);
117 return sb.toString();
120 public String appendDefinitions() {
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:");
131 jb=jb.substring(0, ndx);
132 jb=jb.replaceAll(" +$", "");
134 logger.debug("Value-after: "+jb);
138 sb.append(entry.getValue());
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:");
146 jb=jb.substring(0, ndx);
147 jb=jb.replaceAll(" +$", "");
149 int ndx1=jb.indexOf("resource-version:");
150 logger.debug("Key: "+entry.getKey()+" index: " + ndx1);
151 logger.debug("Value: "+jb);
153 jb=jb.substring(0, ndx1);
154 jb=jb.replaceAll(" +$", "");
156 logger.debug("Value-after: "+jb);
160 sb.append("getDefinitions:\n");
161 for (Map.Entry<String, String> entry : sortedJavaTypeDefinitions.entrySet()) {
162 String jb=entry.getValue().replaceAll("/definitions/", "/getDefinitions/");
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);
181 case "ServiceDesignAndCreation":
183 case "LicenseManagement":
184 case "CloudInfrastructure":
187 logger.debug("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);
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))
250 String elementDescription=xmlElementElement.getPathDescriptionProperty();
251 if(getItemName == null) {
252 addTypeV = xmlElementElement.getAddTypes(v.name());
254 if ( "true".equals(xmlElementElement.getAttribute("xml-key"))) {
255 path += "/{" + xmlElementElement.getAttribute("name") + "}";
257 logger.debug("path: " + path);
258 logger.debug( "xmlElementElement.getAttribute(required):"+xmlElementElement.getAttribute("required") );
260 if ( ("true").equals(xmlElementElement.getAttribute("required"))) {
261 if ( requiredCnt == 0 )
262 sbRequired.append(" required:\n");
264 if ( addTypeV == null || addTypeV.isEmpty()) {
265 sbRequired.append(" - " + xmlElementElement.getAttribute("name") + "\n");
267 for ( int k = 0; k < addTypeV.size(); ++k ) {
268 sbRequired.append(" - " + getXmlRootElementName(addTypeV.elementAt(k)) + ":\n");
273 if ( "true".equals(xmlElementElement.getAttribute("xml-key")) ) {
274 sbParameters.append(xmlElementElement.getPathParamYAML(elementDescription));
276 if ( indexedProps != null
277 && indexedProps.contains(xmlElementElement.getAttribute("name") ) ) {
278 containerProps.add(xmlElementElement.getQueryParamYAML());
279 GetOperation.addContainerProps(container, containerProps);
281 if ( xmlElementElement.isStandardType()) {
282 sbProperties.append(xmlElementElement.getTypePropertyYAML());
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);
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 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");
326 if(addType.equals("AaiInternal")) //Filter out references to AaiInternal
327 sbProperties.append("");
329 sbProperties.append(" " + getXmlRootElementName(addType) + ":\n");
330 sbProperties.append(" type: object\n");
331 sbProperties.append(" $ref: \"#/definitions/" + getXmlRootElementName(addType) + "\"\n");
334 if ( StringUtils.isNotEmpty(elementDescription) )
335 sbProperties.append(" description: " + elementDescription + "\n");
341 if ( sbParameters.toString().length() > 0 ) {
342 if ( pathParams == null )
343 pathParams = new StringBuffer();
344 pathParams.append(sbParameters);
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);
350 PutOperation put = new PutOperation(useOpId, xmlRootElementName, tag, path, pathParams == null ? "" : pathParams.toString(), this.v);
351 pathSb.append(put.toString());
353 PatchOperation patch = new PatchOperation(useOpId, xmlRootElementName, tag, path, pathParams == null ? "" : pathParams.toString());
354 pathSb.append(patch.toString());
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();
366 if ( generatedJavaType.containsKey(xmlRootElementName) ) {
367 logger.debug("xmlRootElementName(1)="+xmlRootElementName);
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");
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);
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);
395 footnotes.add(edgeRuleSet.preventDeleteRules(xmlRootElementName));
396 sbEdge.append(footnotes.toString());
397 validEdges = sbEdge.toString();
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");
406 if ( pathDescriptionProperty != null ) {
407 definitionsSb.append(" " + pathDescriptionProperty + "\n" );
408 definitionsLocalSb.append(" " + pathDescriptionProperty + "\n" );
410 if (validEdges != null) {
411 definitionsSb.append(validEdges);
412 definitionsLocalSb.append(validEdges);
416 if ( requiredCnt > 0 ) {
417 definitionsSb.append(sbRequired);
418 definitionsLocalSb.append(sbRequired);
421 if ( propertyCnt > 0 ) {
422 definitionsSb.append(" properties:\n");
423 definitionsSb.append(sbProperties);
424 definitionsLocalSb.append(" properties:\n");
425 definitionsLocalSb.append(sbProperties);
428 javaTypeDefinitions.put(xmlRootElementName, definitionsLocalSb.toString());
429 } catch (Exception e) {
432 generatedJavaType.put(xmlRootElementName, null);
433 logger.trace("xmlRootElementName(2)="+xmlRootElementName);
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())
445 outfile.createNewFile();
446 } catch (IOException e) {
447 logger.error( "Exception creating output file " + outfileName);
451 try(BufferedWriter bw = Files.newBufferedWriter(Paths.get(outfileName), Charset.forName("UTF-8"))){
452 bw.write(fileContent);
453 } catch ( IOException e) {
454 logger.error( "Exception writing output file " + outfileName);