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(addType == "overloaded-model" || addType.equals("OverloadedModel") || addType == "owning-entity") {
291 logger.debug("Description check: "+ addType+"opId="+opId+" itemName=none");
292 // Log.info("Element name="+xmlElementElement.getAttribute("name"));
296 if ( opId == null || !opId.contains(addType)) {
297 processJavaTypeElementSwagger( addType, getJavaTypeElementSwagger(addType),
298 pathSb, definitionsSb, path, tag == null ? useTag : tag, useOpId, null,
299 newPathParams, validEdges);
301 // need item name of array
302 String itemName = processJavaTypeElementSwagger( addType, getJavaTypeElementSwagger(addType),
303 pathSb, definitionsSb, path, tag == null ? useTag : tag, useOpId,
304 "array", null, null );
306 if ( itemName != null ) {
307 if ( addType.equals("AaiInternal") ) {
308 logger.debug( "addType AaiInternal, skip properties");
310 } else if ( getItemName == null) {
312 sbProperties.append(" " + getXmlRootElementName(addType) + ":\n");
313 sbProperties.append(" type: array\n items:\n");
314 sbProperties.append(" $ref: \"#/definitions/" + (itemName == "" ? "aai-internal" : itemName) + "\"\n");
315 if ( StringUtils.isNotEmpty(elementDescription) )
316 sbProperties.append(" description: " + elementDescription + "\n");
319 if ( ("java.util.ArrayList").equals(xmlElementElement.getAttribute("container-type"))) {
320 // need properties for getXmlRootElementName(addType)
321 newPathParams = new StringBuffer((pathParams == null ? "" : pathParams.toString())+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 if(addType.equals("AaiInternal")) //Filter out references to AaiInternal
333 sbProperties.append("");
335 sbProperties.append(" " + getXmlRootElementName(addType) + ":\n");
336 sbProperties.append(" type: object\n");
337 sbProperties.append(" $ref: \"#/definitions/" + getXmlRootElementName(addType) + "\"\n");
340 if ( StringUtils.isNotEmpty(elementDescription) )
341 sbProperties.append(" description: " + elementDescription + "\n");
347 if ( sbParameters.toString().length() > 0 ) {
348 if ( pathParams == null )
349 pathParams = new StringBuffer();
350 pathParams.append(sbParameters);
352 GetOperation get = new GetOperation(useOpId, xmlRootElementName, tag, path, pathParams == null ? "" : pathParams.toString());
353 pathSb.append(get.toString());
354 logger.debug("opId vs useOpId:"+opId+" vs "+useOpId+" PathParams="+pathParams);
356 PutOperation put = new PutOperation(useOpId, xmlRootElementName, tag, path, pathParams == null ? "" : pathParams.toString(), this.v);
357 pathSb.append(put.toString());
359 PatchOperation patch = new PatchOperation(useOpId, xmlRootElementName, tag, path, pathParams == null ? "" : pathParams.toString());
360 pathSb.append(patch.toString());
362 DeleteOperation del = new DeleteOperation(useOpId, xmlRootElementName, tag, path, pathParams == null ? "" : pathParams.toString());
363 pathSb.append(del.toString());
364 //Write operations by Namespace(tagName)
365 // if(javaTypeName == useTag && tag == null) {
366 // pathSb.append(appendDefinitions());
367 // writeYAMLfile(javaTypeName, pathSb.toString());
368 // pathSb.delete(0, pathSb.length());
369 // javaTypeDefinitions.clear();
370 // generatedJavaType.clear();
372 if ( generatedJavaType.containsKey(xmlRootElementName) ) {
373 logger.debug("xmlRootElementName(1)="+xmlRootElementName);
377 definitionsSb.append(" " + xmlRootElementName + ":\n");
378 definitionsLocalSb.append(" " + xmlRootElementName + ":\n");
379 Collection<EdgeDescription> edges = edgeRuleSet.getEdgeRules(xmlRootElementName );
380 DeleteFootnoteSet footnotes = new DeleteFootnoteSet(xmlRootElementName);
381 if ( edges.size() > 0 ) {
382 StringBuffer sbEdge = new StringBuffer();
383 sbEdge.append(" ###### Related Nodes\n");
385 for (EdgeDescription ed : edges) {
386 if ( ed.getRuleKey().startsWith(xmlRootElementName)) {
387 sbEdge.append(" - TO ").append(ed.getRuleKey().substring(ed.getRuleKey().indexOf("|")+1));
388 String footnote = ed.getAlsoDeleteFootnote(xmlRootElementName);
389 sbEdge.append(ed.getRelationshipDescription("TO", xmlRootElementName)+footnote+"\n");
390 if(StringUtils.isNotEmpty(footnote)) footnotes.add(footnote);
393 for (EdgeDescription ed : edges) {
394 if ( ed.getRuleKey().endsWith(xmlRootElementName)) {
395 sbEdge.append(" - FROM ").append(ed.getRuleKey().substring(0, ed.getRuleKey().indexOf("|")));
396 String footnote = ed.getAlsoDeleteFootnote(xmlRootElementName);
397 sbEdge.append(ed.getRelationshipDescription("FROM", xmlRootElementName)+footnote+"\n");
398 if(StringUtils.isNotEmpty(footnote)) footnotes.add(footnote);
401 footnotes.add(edgeRuleSet.preventDeleteRules(xmlRootElementName));
402 sbEdge.append(footnotes.toString());
403 validEdges = sbEdge.toString();
406 // Handle description property. Might have a description OR valid edges OR both OR neither.
407 // Only put a description: tag if there is at least one.
408 if (pathDescriptionProperty != null || validEdges != null) {
409 definitionsSb.append(" description: |\n");
410 definitionsLocalSb.append(" description: |\n");
412 if ( pathDescriptionProperty != null ) {
413 definitionsSb.append(" " + pathDescriptionProperty + "\n" );
414 definitionsLocalSb.append(" " + pathDescriptionProperty + "\n" );
416 if (validEdges != null) {
417 definitionsSb.append(validEdges);
418 definitionsLocalSb.append(validEdges);
422 if ( requiredCnt > 0 ) {
423 definitionsSb.append(sbRequired);
424 definitionsLocalSb.append(sbRequired);
427 if ( propertyCnt > 0 ) {
428 definitionsSb.append(" properties:\n");
429 definitionsSb.append(sbProperties);
430 definitionsLocalSb.append(" properties:\n");
431 definitionsLocalSb.append(sbProperties);
434 javaTypeDefinitions.put(xmlRootElementName, definitionsLocalSb.toString());
435 } catch (Exception e) {
438 generatedJavaType.put(xmlRootElementName, null);
439 logger.trace("xmlRootElementName(2)="+xmlRootElementName);
443 private void writeYAMLfile(String outfileName, String fileContent) {
444 outfileName = (StringUtils.isEmpty(outfileName)) ? "aai_swagger" : outfileName;
445 outfileName = (outfileName.lastIndexOf(File.separator) == -1) ? yaml_dir + File.separator +outfileName+"_" + v.name() + "." + generateTypeYAML : outfileName;
446 File outfile = new File(outfileName);
447 File parentDir = outfile.getParentFile();
448 if(parentDir != null && ! parentDir.exists())
451 outfile.createNewFile();
452 } catch (IOException e) {
453 logger.error( "Exception creating output file " + outfileName);
456 BufferedWriter bw = null;
458 Charset charset = Charset.forName("UTF-8");
459 Path path = Paths.get(outfileName);
460 bw = Files.newBufferedWriter(path, charset);
461 bw.write(fileContent);
465 } catch ( IOException e) {
466 logger.error( "Exception writing output file " + outfileName);