From a2cdfc1a17e69f7da4971ea3cfc43c9993992879 Mon Sep 17 00:00:00 2001 From: Alex Honor Date: Sun, 29 Jul 2012 17:17:09 -0700 Subject: [PATCH] Refactored import implementation to be service-based. Controller layer now works just as a web gateway. Fixes #180 --- grails-app/conf/import/example1.xml | 120 ++++++++ grails-app/conf/import/example2.xml | 182 ++++++++++++ grails-app/conf/import/rundeck.xml | 42 +++ grails-app/conf/import/yana.xsd | 140 +++++++++ .../com/dtolabs/ImportController.groovy | 277 +++++------------- .../services/com/dtolabs/ImportService.groovy | 213 ++++++++++++++ .../com/dtolabs/WebhookService.groovy | 10 +- .../com/dtolabs/ImportControllerTests.groovy | 118 ++++++++ .../com/dtolabs/ImportServiceTests.groovy | 108 +++++++ 9 files changed, 1001 insertions(+), 209 deletions(-) create mode 100644 grails-app/conf/import/example1.xml create mode 100644 grails-app/conf/import/example2.xml create mode 100644 grails-app/conf/import/rundeck.xml create mode 100644 grails-app/conf/import/yana.xsd create mode 100644 grails-app/services/com/dtolabs/ImportService.groovy create mode 100644 test/unit/com/dtolabs/ImportControllerTests.groovy create mode 100644 test/unit/com/dtolabs/ImportServiceTests.groovy diff --git a/grails-app/conf/import/example1.xml b/grails-app/conf/import/example1.xml new file mode 100644 index 0000000..b4cd2a3 --- /dev/null +++ b/grails-app/conf/import/example1.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + this is the server description + Node.png + + + + + + + + + + + + + + + + + + + This is the software description + Package.png + + + + + + + This is the location description + Site.png + + + + + + + + + + + + + This is a server + + Test_Server + Dell + dps-256 + 12345 + 127.0.0.1 + 127.0.0.1 + 2 + 4GB + 2TB + 10am + 12pm + Ubuntu_Test_Server + Linux + hostname.test.com + + + + This is a software attached to the server + + 1.0 + 1.234.56.7 + + + + This is a location attached to the server + + Rackspace + Berkeley + CA + USA + 94703 + + + + + + + + + + + + + + + diff --git a/grails-app/conf/import/example2.xml b/grails-app/conf/import/example2.xml new file mode 100644 index 0000000..68ffde8 --- /dev/null +++ b/grails-app/conf/import/example2.xml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An application host + Node.png + + + + + + + + + + + + + + + + + + A software package + Package.png + + + + + + + + + + + + + + + + + + + A software service + Service.png + + + + + + + + + + A service site + Site.png + + + + + + + + + + The ubuntu server + + ubuntu + i386 + unix + Linux + 2.6.32-28-generic + alexh + 18080 + /tmp/demo/tomcat + simpleapp + tomcat + qa + + + + + The tomcat service + + /tmp/demo/tomcat + 1 + tomcat + 18080 + + + + + The QA site + + 1 + + + + + the tomcat container + + no-arch + apache-tomcat-5.5.31 + apache-tomcat-5.5.31.zip + zip + /tmp/demo/tomcat + 1 + http://localhost/simpleapp/apache-tomcat-5.5.31.zip + 5.5.31 + zip + + + + + + the simple webapp + + no-arch + simple + simple-1.0.0.war + war + /tmp/demo/tomcat/webapps + 1 + http://localhost/simpleapp/simple-1.0.0.war + 1.0.0 + war + + + + + + + + + + + + + + + + + + + diff --git a/grails-app/conf/import/rundeck.xml b/grails-app/conf/import/rundeck.xml new file mode 100644 index 0000000..bd40269 --- /dev/null +++ b/grails-app/conf/import/rundeck.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + Rundeck node (system/server) type + Node.png + + + + + + + + + + + + + + Sample node instance + + centos62-rundeck-tomcat + amd64 + unix + Linux + 2.6.32-220.el6.x86_64 + tomcat + + + + diff --git a/grails-app/conf/import/yana.xsd b/grails-app/conf/import/yana.xsd new file mode 100644 index 0000000..3bdc8a5 --- /dev/null +++ b/grails-app/conf/import/yana.xsd @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/grails-app/controllers/com/dtolabs/ImportController.groovy b/grails-app/controllers/com/dtolabs/ImportController.groovy index c661e11..a406c24 100644 --- a/grails-app/controllers/com/dtolabs/ImportController.groovy +++ b/grails-app/controllers/com/dtolabs/ImportController.groovy @@ -1,218 +1,81 @@ package com.dtolabs -import java.util.Date; -import java.util.Set; - -//import javax.xml.XMLConstants -import javax.xml.transform.stream.StreamSource -import javax.xml.validation.Schema -import javax.xml.validation.SchemaFactory -import javax.xml.validation.Validator -import com.dtolabs.Attribute -import com.dtolabs.ChildNode -import com.dtolabs.Filter -import com.dtolabs.NodeType -import com.dtolabs.Node -import com.dtolabs.NodeTypeRelationship -import com.dtolabs.NodeAttribute -import com.dtolabs.NodeValue + import grails.plugins.springsecurity.Secured -import org.xml.sax.SAXException -@Secured(['ROLE_YANA_ADMIN','ROLE_YANA_SUPERUSER']) + +@Secured(['ROLE_YANA_ADMIN', 'ROLE_YANA_SUPERUSER']) class ImportController { - def springSecurityService - def webhookService - def projectService - - def api(){ - switch(request.method){ - case "POST": - def json = request.JSON - this.savexml() - break - case "GET": - //this.show() - break - case "PUT": - //this.update() - break - case "DELETE": - //this.delete() - break - } - } - - def index() {} - - def importxml() { - [projectList:projectService.listProjects()] + def springSecurityService + def webhookService + def projectService + def importService + + def api() { + switch (request.method) { + case "POST": + this.savexml() + break + } + } + + def index() { + redirect action: 'importxml' + } + + def importxml() { + [projectList: projectService.listProjects()] } - - - // all children are aspects of groovy.util.slurpersupport.NodeChild - // this allows us to pass them as NodeChild objects - def savexml() { + + + def savexml() { + + def nodes = [] + def project = Project.findByName(params.project) - if(!project){ - request.message = message(code: 'default.not.found.message', args: [params.project], default: "Project {0} was not found") + if (!project) { + request.message = message(code: 'default.not.found.message', + args: [params.project], + default: "Project {0} was not found") + return redirect(action: 'importxml') } - if(!request.getFile("yanaimport").empty){ - def xml = new XmlSlurper().parse(request.getFile("yanaimport").inputStream) - SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); - def xsdIn= servletContext.getResourceAsStream("/xsd/yana.xsd") - Schema schema = factory.newSchema(new StreamSource(xsdIn)) - Validator validator = schema.newValidator() - - Date now = new Date() - - try{ - // attempt to validate first - validator.validate(new StreamSource(request.getFile("yanaimport").inputStream)) - - // parse attributes - xml.attributes.children().each{ attribute -> - Attribute att = Attribute.findByProjectAndName(project,attribute.@id.toString()) - if(!att){ - //get dependencies first - //println("attribute class:"+attribute.getClass()) - - Filter filter = Filter.findByProjectAndDataType(project,attribute.@filter.toString()) - - att = new Attribute() - att.project= project - att.name = attribute.@id - att.filter = filter - att.dateCreated = new Date() - att.dateModified = new Date() - att.description='' - - att.save(flush: true,failOnError:true) - } - } - - // parse nodetypes and nodeattributes - xml.nodetypes.children().each{ nodetype -> - //TODO: project in query - NodeType ntype = NodeType.findByProjectAndName(project,nodetype.@id.toString()) - if(!ntype){ - //println("nodetype class name:"+nodetype.getClass()) - - ntype = new NodeType() - ntype.project= project - ntype.name = nodetype.@id - ntype.description=nodetype.description.text() - ntype.image=nodetype.image.text() - ntype.dateCreated = new Date() - ntype.dateModified = new Date() - - ntype.save(flush: true,failOnError:true) - } - def order = 1 - nodetype.nodeAttributes.children().each{ nodeAttribute -> - Attribute attribute = Attribute.findByProjectAndName(project,nodeAttribute.@attribute.toString()) - NodeAttribute ta = NodeAttribute.findByNodetypeAndAttribute(ntype,attribute) - if(!ta){ - ta = new NodeAttribute() - ta.nodetype = ntype - ta.attribute = attribute - ta.required = nodeAttribute.@required.toString().toBoolean(); - //ta.order = order - ta.save(flush: true,failOnError:true) - order++ - } - } - } - Node nd - // parse nodes and attributevalues - xml.nodes.children().each{ node -> - nd = Node.findByProjectAndName(project,node.@id.toString()) - NodeType nodetype = NodeType.findByProjectAndName(project,node.@nodetype.toString()) - if(!nd){ - nd = new Node() - nd.project= project - nd.name = node.@id - nd.description = node.description.toString() - nd.tags = node.@tags.toString() - nd.nodetype = nodetype - nd.dateCreated = new Date() - nd.dateModified = new Date() - nd.save(flush: true,failOnError:true) - }else{ - NodeValue.executeUpdate("delete NodeValue TV where TV.node = ?", [nd]) - } - - node.values.children().each{ nodeValue -> - def nodeAttribute = nodeValue.@nodeAttribute.toString() - def att = xml.nodetypes.nodetype.nodeAttributes.nodeAttribute.findAll { it.@id.text()==nodeAttribute } - Attribute attribute = Attribute.findByProjectAndName(project,att.@attribute.toString()) - NodeAttribute ta = NodeAttribute.findByNodetypeAndAttribute(nodetype,attribute) - - NodeValue tv = new NodeValue() - tv.node = nd - tv.nodeattribute = ta - tv.value = nodeValue.toString() - tv.dateCreated = new Date() - tv.dateModified = new Date() - tv.save(flush: true,failOnError:true) - } - } - - // parse nodetype parent/child - xml.nodetyperelationships.children().each{ nodetypechild -> - // get dependencies - NodeType parent = NodeType.findByProjectAndName(project,nodetypechild.@parent.toString()) - NodeType child = NodeType.findByProjectAndName(project,nodetypechild.@child.toString()) - - NodeTypeRelationship childnodetype = NodeTypeRelationship.findByChildAndParent(child,parent) - if(!childnodetype){ - childnodetype = new NodeTypeRelationship() - childnodetype.roleName = nodetypechild.@rolename.toString() - childnodetype.child = child - childnodetype.parent = parent - childnodetype.save(flush: true,failOnError:true) - } - } - - // parse node parent/child - xml.noderelationships.children().each{ nodechild -> - // get dependencies - Node parent = Node.findByProjectAndName(project,nodechild.@parent.toString()) - Node child = Node.findByProjectAndName(project,nodechild.@child.toString()) - - Node[] childNodeTypes = Node.findAllByNodetype(child.nodetype) - Node[] parentNodeTypes = Node.findAllByNodetype(parent.nodetype) - - NodeTypeRelationship childnodetype = NodeTypeRelationship.findByChildAndParent(child.nodetype,parent.nodetype) - - ChildNode childnode = ChildNode.findByChildAndParent(child,parent) - if(!childnode && childnodetype){ - childnode = new ChildNode() - childnode.relationshipName = nodechild.@relationshipname.toString() - childnode.child = child - childnode.parent = parent - childnode.save(flush: true,failOnError:true) - }else{ - //throw new SAXException( "Nodechild relationship not within bounds as described by nodetypechild." ) - } - } - - ArrayList nodes = [nd] - webhookService.postToURL('node', nodes,'create') - - } catch (SAXException e) { - flash.message = "Error in xml schema: " + e.message - redirect(action: "importxml") - } - }else{ - flash.message = "Import file cannot be empty. Please try again. " - redirect(action: "importxml") - } - - - - - } + + /** + * Get the user's file upload. + */ + def importFile = request.getFile("yanaimport") + + log.debug("importFile name: " + importFile.name + + ", content-type: " + importFile.contentType + + ", fileName: " + importFile.originalFilename) + + /** + * Validate and then populate the project's model + */ + if (!importFile.empty) { + + try { + // validate the XML input data + importService.validate(importFile.inputStream) + + // populate the project + nodes = importService.populate( + importFile.inputStream, project) + log.info("imported node count: " + nodes.size()) + webhookService.postToURL('node', nodes, 'create') + + } catch (ImportServiceException e) { + log.error("Error importing document: " + e.message) + flash.message = "Error importing document: " + e.message + redirect(action: "importxml") + } + } else { + flash.message = "Import file cannot be empty. Please try again." + redirect(action: "importxml") + } + // return the node list as the model to the view + [nodes: nodes] + } } diff --git a/grails-app/services/com/dtolabs/ImportService.groovy b/grails-app/services/com/dtolabs/ImportService.groovy new file mode 100644 index 0000000..d0c0129 --- /dev/null +++ b/grails-app/services/com/dtolabs/ImportService.groovy @@ -0,0 +1,213 @@ +package com.dtolabs + +import javax.xml.validation.Schema +import javax.xml.validation.SchemaFactory +import javax.xml.validation.Validator +import javax.xml.transform.stream.StreamSource +import org.xml.sax.SAXException + +/** + * Provides a model import service that takes file uploads + * of XML-based model content and stores it in a project. + * + * Import format must conform to the /import/yana.xsd file. + */ +class ImportService { + + + /** + * Validate the input XML conforms to the yana XSD. + * @param xml The XML content containing yana model data + */ + def validate(final InputStream xml) { + if (null==xml) throw new ImportServiceException("XML content was empty") + + def InputStream xsd = getClass().getResourceAsStream("/import/yana.xsd") + if (null==xsd) throw new ImportServiceException("Could not load yana.xsd from classpath") + + SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); + Schema schema = factory.newSchema(new StreamSource(xsd)) + Validator validator = schema.newValidator() + + try { + + validator.validate(new StreamSource(xml)) + + } catch (SAXException e) { + + throw new ImportServiceException("Invalid XML content", e); + + } + } + + /** + * Read an input stream for an XML import and populate the model with its contents. + * TODO: Refactor to use better separated collaboration. Encapsulate with domain class methods. + * @param xmlInput InputStream containing yana XML data + * @param project Project to store the model data + */ + def populate(InputStream xmlInput, Project project) { + if (null == xmlInput) throw new ImportServiceException("XML content stream was null") + + // Nodes list to return + def results = [] + + // Store parsed XML GPath result + def xml + + try { + + // parse the XML content from the InputStream + xml = new XmlSlurper().parse(xmlInput) + + } catch (SAXException e) { + throw new ImportServiceException("Error parsing XML input", e) + } + + try { + // read Attributes + xml.attributes.children().each { attribute -> + Attribute att = Attribute.findByProjectAndName(project, attribute.@id.toString()) + if (!att) { + att = new Attribute(project: project, dateCreated: new Date()) + } + Filter filter = Filter.findByProjectAndDataType(project, attribute.@filter.toString()) + if (null == filter) { + throw new ImportServiceException("No Filter defined for data type: " + + attribute.@filter.toString()) + } + att.filter = filter + att.name = attribute.@id + att.dateModified = new Date() + att.description = attribute.@description.toString() + + att.save(flush: true, failOnError: true) + } + + // read NodeTypes and NodeAttributes + xml.nodetypes.children().each { nodetype -> + + NodeType ntype = NodeType.findByProjectAndName(project, nodetype.@id.toString()) + if (!ntype) { + ntype = new NodeType(project: project, dateCreated: new Date()) + } + ntype.name = nodetype.@id + ntype.description = nodetype.description.text() + ntype.image = nodetype.image.text() + ntype.dateModified = new Date() + + ntype.save(flush: true, failOnError: true) + + nodetype.nodeAttributes.children().each { nodeAttribute -> + Attribute attribute = Attribute.findByProjectAndName(project, + nodeAttribute.@attribute.toString()) + NodeAttribute na = NodeAttribute.findByNodetypeAndAttribute(ntype, attribute) + if (!na) { + na = new NodeAttribute(nodetype: ntype) + } + na.attribute = attribute + na.required = nodeAttribute.@required.toString().toBoolean(); + + na.save(flush: true, failOnError: true) + } + } + Node nd + // read Nodes and NodeValues + xml.nodes.children().each { node -> + nd = Node.findByProjectAndName(project, node.@id.toString()) + NodeType nodetype = NodeType.findByProjectAndName(project, node.@nodetype.toString()) + if (!nd) { + nd = new Node(project: project, dateCreated: new Date()) + } else { + NodeValue.executeUpdate("delete NodeValue NV where NV.node = ?", [nd]) + } + nd.name = node.@id + nd.description = node.description.toString() + nd.tags = node.@tags.toString() + nd.nodetype = nodetype + nd.dateModified = new Date() + nd.save(flush: true, failOnError: true) + + node.values.children().each { nodeValue -> + def nodeAttribute = nodeValue.@nodeAttribute.toString() + def att = xml.nodetypes.nodetype.nodeAttributes.nodeAttribute.findAll { + it.@id.text() == nodeAttribute + } + Attribute attribute = Attribute.findByProjectAndName(project, + att.@attribute.toString()) + if (null == attribute) { + throw new ImportServiceException("attribute not found " + att.@attribute) + } + NodeAttribute na = NodeAttribute.findByNodetypeAndAttribute(nodetype, attribute) + + NodeValue value = new NodeValue(node: nd, + nodeattribute: na, value: nodeValue.toString(), + dateCreated: new Date(), dateModified: new Date() + ) + + value.save(flush: true, failOnError: true) + } + results += nd + } + + // read NodeTypeRelationships + xml.nodetyperelationships.children().each { typerel -> + + NodeType parent = NodeType.findByProjectAndName(project, typerel.@parent.toString()) + NodeType child = NodeType.findByProjectAndName(project, typerel.@child.toString()) + + NodeTypeRelationship relationship = NodeTypeRelationship.findByChildAndParent( + child, parent) + if (!relationship) { + relationship = new NodeTypeRelationship( + roleName: typerel.@rolename.toString(), + child: child, parent: parent + ) + + relationship.save(flush: true, failOnError: true) + } + } + + // read ChildNodes + xml.noderelationships.children().each { nodechild -> + + Node parent = Node.findByProjectAndName(project, nodechild.@parent.toString()) + Node child = Node.findByProjectAndName(project, nodechild.@child.toString()) + + NodeTypeRelationship relationship = NodeTypeRelationship.findByChildAndParent( + child.nodetype, parent.nodetype) + + ChildNode childnode = ChildNode.findByChildAndParent(child, parent) + if (!childnode && relationship) { + childnode = new ChildNode(child: child, parent: parent) + childnode.relationshipName = nodechild.@relationshipname.toString() + + childnode.save(flush: true, failOnError: true) + } + } + + } catch (SAXException e) { + + throw new ImportServiceException("Error processing input", e) + } + + return results + } +} + +/** + * Exception thrown by the ImportService + */ +class ImportServiceException extends RuntimeException { + + def ImportServiceException() { + } + + def ImportServiceException(e) { + super(e); + } + + def ImportServiceException(e, throwable) { + super(e, throwable); + } +} diff --git a/grails-app/services/com/dtolabs/WebhookService.groovy b/grails-app/services/com/dtolabs/WebhookService.groovy index 6edffbb..30f5c2b 100644 --- a/grails-app/services/com/dtolabs/WebhookService.groovy +++ b/grails-app/services/com/dtolabs/WebhookService.groovy @@ -13,8 +13,14 @@ class WebhookService { static scope = "prototype" def postToURL(String service, ArrayList data, String state) { - // set attempts number in config.properties so we can override - def hooks = Webhook.findAll("from Webhook where service='${service}' and attempts<5") + + + def c = Webhook.createCriteria() + def hooks = c { + eq("service", service) + //TODO: set attempts max in config.properties so we can override + between("attempts", 0, 5) + } hooks.each { hook -> try{ String hookData diff --git a/test/unit/com/dtolabs/ImportControllerTests.groovy b/test/unit/com/dtolabs/ImportControllerTests.groovy new file mode 100644 index 0000000..81ec863 --- /dev/null +++ b/test/unit/com/dtolabs/ImportControllerTests.groovy @@ -0,0 +1,118 @@ +package com.dtolabs + + +import grails.test.mixin.* +import org.springframework.mock.web.MockMultipartFile +import org.springframework.mock.web.MockMultipartHttpServletRequest +import org.springframework.core.io.Resource +import org.springframework.core.io.ClassPathResource + +/** + * Created with IntelliJ IDEA. + * User: alexh + * Date: 7/27/12 + * Time: 10:29 AM + * To change this template use File | Settings | File Templates. + */ + +@TestFor(ImportController) +@Mock([Project, Attribute, Filter, NodeType, NodeTypeRelationship, +Node, NodeValue, NodeAttribute, ChildNode,Webhook]) +class ImportControllerTests { + + /** + * Validate index redirects to import page + */ + void testIndex() { + controller.index() + assert response.redirectedUrl == '/import/importxml' + } + + def example1 = """ + + + + + + + host type + Node.png + + + + + + + + + Sample node instance + + centos62-rundeck-tomcat + + + + + """ + + /** + * Check the XML upload and import work + */ + void testSavexml() { + + // Auto-wire in the ImportService and WebhookService used by this controller. + defineBeans { + importService(ImportService) + webhookService(WebhookService) + } + assertNotNull("ImportService not auto-wired into the test", controller.importService) + + /** + * + * Create a test project to store a model + */ + Project project = new Project(name: 'test1', description: 'desc').save() + + // Set the query string with the project parameter + params.project = 'test1' + /** + * Declare supporting filter used in this example. + */ + new Filter(project: project, dataType: "String", regex: ".*").save() + + /** + * Read in the XML model data and add it to the request. + */ + def contentStream = new StringBufferInputStream(example1) + request.addFile( + new MockMultipartFile('yanaimport', 'example1.xml', 'text/xml', contentStream) + ) + + /** + * Run the controller action + */ + controller.savexml() + + /** + * Confirm the model was populated + */ + def host = NodeType.findByProjectAndName(project, "host") + assertNotNull("Imported NodeType not found in the model", host) + assertEquals("NodeType name did not match", host.name, "host") + def host1 = Node.findByProjectAndName(project, "host1") + assertEquals("Node name did not match", host1.name, "host1") + def hostname = Attribute.findByProjectAndName(project, "hostname") + assertEquals("Attribute name did not match", hostname.name, "hostname") + } + + /** + * Utility method to lookup a resource and return it as an InputStream + * @param filename Filename to check in the classpath + * @return InputStream + */ + private InputStream asInputStream(String filename) { + final Resource resource = new ClassPathResource(filename, getClass().classLoader) + final File f = resource.getFile() + return new FileInputStream(f) + } + +} diff --git a/test/unit/com/dtolabs/ImportServiceTests.groovy b/test/unit/com/dtolabs/ImportServiceTests.groovy new file mode 100644 index 0000000..e9a091d --- /dev/null +++ b/test/unit/com/dtolabs/ImportServiceTests.groovy @@ -0,0 +1,108 @@ +package com.dtolabs + + + +import grails.test.mixin.* + +import grails.test.GrailsUnitTestCase +import org.springframework.core.io.Resource +import org.springframework.core.io.ClassPathResource + +/** + * See the API for {@link grails.test.mixin.services.ServiceUnitTestMixin} for usage instructions + */ +@TestFor(ImportService) +@Mock([Project, Attribute, Filter, NodeType, NodeTypeRelationship, +Node, NodeValue, NodeAttribute, ChildNode]) +class ImportServiceTests { + + /** + * Validate a model import file + */ + void testValidate() { + + /** + * Verify the model in the XML conforms to the allowed schema + */ + service.validate(asInputStream("/import/example2.xml")) + + } + + /** + * Test the population of a model + */ + void testPopulate() { + + /** + * Create a project to store a model + */ + Project project = new Project(name: 'test1', description: 'desc').save() + /** + * Input does not declare any filters yet so define a couple. + */ + new Filter(project: project, dataType: "String", regex: ".*").save() + new Filter(project: project, dataType: "URL", regex: ".*").save() + + /* + * Parse the XML input file and populate the model + */ + def nodes = service.populate(asInputStream("/import/example2.xml"), project) + assertEquals("incorrect number of nodes: " + nodes.size(), nodes.size(), 5) + + /** + * Verify the objects have been populated in the model + */ + test: { // Attribute + def arch = Attribute.findByProjectAndName(project, "arch") + assertEquals("Attribute name did not match", arch.name, "arch") + } + test: { // NodeType + def host = NodeType.findByProjectAndName(project, "Host") + assertEquals("NodeType name did not match", host.name, "Host") + def pkg = NodeType.findByProjectAndName(project, "Package") + assertEquals("NodeType name did not match", pkg.name, "Package") + def svc = NodeType.findByProjectAndName(project, "Service") + assertEquals("NodeType name did not match", svc.name, "Service") + def site = NodeType.findByProjectAndName(project, "Site") + assertEquals("NodeType name did not match", site.name, "Site") + } + test: { // Node + def ubuntu = Node.findByProjectAndName(project, "ubuntu") + assertEquals("Node name did not match", ubuntu.name, "ubuntu") + def tomcat = Node.findByProjectAndName(project, "tomcat") + assertEquals("Node name did not match", tomcat.name, "tomcat") + def qa = Node.findByProjectAndName(project, "qa") + assertEquals("Node name did not match", qa.name, "qa") + def apachetom = Node.findByProjectAndName(project, "apache-tomcat-5.5.31") + assertEquals("Node name did not match", apachetom.name, "apache-tomcat-5.5.31") + } + test: { // NodeTypeRelationship + def environment = NodeTypeRelationship.findByRoleName("environment") + assertEquals("NodeTypeRelationship name did not match", environment.roleName, "environment") + def service = NodeTypeRelationship.findByRoleName("service") + assertEquals("NodeTypeRelationship name did not match", service.roleName, "service") + def pack = NodeTypeRelationship.findByRoleName("package") + assertEquals("NodeTypeRelationship name did not match", pack.roleName, "package") + } + test: { // ChildNode + def qa = ChildNode.findByRelationshipName("QA") + assertEquals("ChildNode name did not match", qa.relationshipName, "QA") + def server = ChildNode.findByRelationshipName("server") + assertEquals("ChildNode name did not match", server.relationshipName, "server") + def zip = ChildNode.findByRelationshipName("zip") + assertEquals("ChildNode name did not match", zip.relationshipName, "zip") + } + } + + /** + * Utility method to lookup a resource and return it as an InputStream + * @param filename Filename to check in the classpath + * @return InputStream + */ + private InputStream asInputStream(String filename) { + final Resource resource = new ClassPathResource(filename, getClass().classLoader) + final File f = resource.getFile() + return new FileInputStream(f) + } + +}