diff --git a/archetype-submodule-owa/pom.xml b/archetype-submodule-owa/pom.xml new file mode 100644 index 00000000..480c4a9c --- /dev/null +++ b/archetype-submodule-owa/pom.xml @@ -0,0 +1,38 @@ + + + + openmrs-sdk + org.openmrs.maven + 3.4.8-SNAPSHOT + + 4.0.0 + + org.openmrs.maven.archetypes + openmrs-sdk-archetype-submodule-owa + 3.4.8-SNAPSHOT + maven-archetype + + OpenMRS OWA Submodule Maven Archetype + + + + + org.apache.maven.archetype + archetype-packaging + 2.2 + + + + + + + maven-archetype-plugin + 2.2 + + + + + + \ No newline at end of file diff --git a/archetype-submodule-owa/src/main/resources/META-INF/maven/archetype-metadata.xml b/archetype-submodule-owa/src/main/resources/META-INF/maven/archetype-metadata.xml new file mode 100644 index 00000000..272399d6 --- /dev/null +++ b/archetype-submodule-owa/src/main/resources/META-INF/maven/archetype-metadata.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + assembly.xml + + + + + diff --git a/archetype-submodule-owa/src/main/resources/archetype-resources/assembly.xml b/archetype-submodule-owa/src/main/resources/archetype-resources/assembly.xml new file mode 100644 index 00000000..f10fba9b --- /dev/null +++ b/archetype-submodule-owa/src/main/resources/archetype-resources/assembly.xml @@ -0,0 +1,21 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) + + false + dist + + zip + + + + dist + . + + **/* + * + + + + \ No newline at end of file diff --git a/archetype-submodule-owa/src/main/resources/archetype-resources/pom.xml b/archetype-submodule-owa/src/main/resources/archetype-resources/pom.xml new file mode 100644 index 00000000..f15742fe --- /dev/null +++ b/archetype-submodule-owa/src/main/resources/archetype-resources/pom.xml @@ -0,0 +1,76 @@ + + + 4.0.0 + + ${moduleArtifactId}-owa + ${groupId} + ${version} + pom + + + + + com.github.eirslett + frontend-maven-plugin + 1.0 + + + + install node and npm + + install-node-and-npm + + validate + + v5.3.0 + 3.9.6 + + + + + npm install + + npm + + generate-resources + + install + + + + + npm build + + npm + + generate-resources + + run build:prod + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + zip-owa + + single + + package + + + assembly.xml + + + + + + + + + + diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/AddFeature.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/AddFeature.java new file mode 100644 index 00000000..dbbeed6f --- /dev/null +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/AddFeature.java @@ -0,0 +1,90 @@ +package org.openmrs.maven.plugins; + +import edu.emory.mathcs.backport.java.util.Arrays; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.logging.SystemStreamLog; +import org.openmrs.maven.plugins.utility.OwaHelper; +import org.openmrs.maven.plugins.utility.Project; +import org.openmrs.maven.plugins.utility.SDKConstants; +import org.openmrs.maven.plugins.utility.XmlHelper; + +import java.io.File; +import java.util.List; +import java.util.Properties; + +import static org.twdata.maven.mojoexecutor.MojoExecutor.artifactId; +import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration; +import static org.twdata.maven.mojoexecutor.MojoExecutor.element; +import static org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo; +import static org.twdata.maven.mojoexecutor.MojoExecutor.executionEnvironment; +import static org.twdata.maven.mojoexecutor.MojoExecutor.goal; +import static org.twdata.maven.mojoexecutor.MojoExecutor.groupId; +import static org.twdata.maven.mojoexecutor.MojoExecutor.plugin; +import static org.twdata.maven.mojoexecutor.MojoExecutor.version; + +/** + * @goal add-feature + * @requiresProject false + */ +public class AddFeature extends AbstractTask { + + public static final String OPEN_WEB_APP = "Open Web App"; + public static final String[] OPTIONS = {OPEN_WEB_APP}; + + /** + * feature user wants to add + * + * @parameter expression="${feature}" + */ + private String feature; + + @Override + public void executeTask() throws MojoExecutionException, MojoFailureException { + if (Project.hasProject(new File(System.getProperty("user.dir")))) { + feature = wizard.promptForMissingValueWithOptions("What feature would you like to add?", feature, "feature", Arrays.asList(OPTIONS), null, null); + if(feature.equals(OPEN_WEB_APP) || feature.equals("owa")){ + addOwaSubmodule(); + } else { + throw new IllegalArgumentException("Adding feature "+feature+" is not available. Available features: "+OPTIONS); + } + } else { + throw new IllegalArgumentException("No project found in this directory. Please enter project's main directory and run this command again"); + } + } + + private void addOwaSubmodule() throws MojoExecutionException { + //apply changes to config.xml and main pom.xml + wizard.showMessage("Modifying pom.xml files..."); + new XmlHelper().modifyXml(new File(mavenProject.getBasedir(), "omod"+File.separator+"pom.xml"), "archetype-submodule-owa/omod.pom.xml"); + new XmlHelper().modifyXml(new File(mavenProject.getBasedir(), "omod"+File.separator+"src"+File.separator+"main"+File.separator+"resources"+File.separator+"config.xml"), "archetype-submodule-owa/config.xml"); + + //run archetype to create skeleton configuration for maven owa submodule + Properties properties = new Properties(); + properties.setProperty("artifactId", "owa"); + properties.setProperty("moduleArtifactId", mavenProject.getArtifactId()); + properties.setProperty("groupId", mavenProject.getGroupId()); + properties.setProperty("package", "owa"); + properties.setProperty("version", mavenProject.getVersion()); + mavenSession.getExecutionProperties().putAll(properties); + + wizard.showMessage("Creating OWA submodule..."); + executeMojo( + plugin( + groupId(SDKConstants.PLUGIN_ARCHETYPE_GROUP_ID), + artifactId(SDKConstants.PLUGIN_ARCHETYPE_ARTIFACT_ID), + version(SDKConstants.PLUGIN_ARCHETYPE_VERSION) + ), + goal("generate"), configuration( + element("interactiveMode", "false"), + element("archetypeArtifactId", "openmrs-sdk-archetype-submodule-owa"), + element("archetypeGroupId", "org.openmrs.maven.archetypes"), + element("archetypeVersion", SDKConstants.getSDKInfo().getVersion()) + ), + executionEnvironment(mavenProject, mavenSession, pluginManager)); + + new OwaHelper(mavenSession, mavenProject, pluginManager, wizard) + .setInstallationDir(new File(mavenProject.getBasedir(),"owa")) + .createOwaProject(); + } +} diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/OwaHelper.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/OwaHelper.java index 4b140db3..45537150 100644 --- a/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/OwaHelper.java +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/OwaHelper.java @@ -6,7 +6,6 @@ import org.apache.maven.plugin.BuildPluginManager; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.project.MavenProject; -import org.openmrs.maven.plugins.utility.Wizard; import org.twdata.maven.mojoexecutor.MojoExecutor; import java.io.File; @@ -31,12 +30,20 @@ public class OwaHelper { private MavenSession session; + private File installationDir; + private MavenProject mavenProject; private BuildPluginManager pluginManager; private final Wizard wizard; + //enable chaining + public OwaHelper setInstallationDir(File installationDir) { + this.installationDir = installationDir; + return this; + } + public OwaHelper(MavenSession session, MavenProject mavenProject, BuildPluginManager pluginManager, Wizard wizard) { this.session = session; this.mavenProject = mavenProject; @@ -45,7 +52,7 @@ public OwaHelper(MavenSession session, MavenProject mavenProject, BuildPluginMan } public void createOwaProject() throws MojoExecutionException { - File owaDir = prepareOwaDir(); + File owaDir = installationDir != null ? installationDir : prepareOwaDir(); wizard.showMessage("Creating OWA project in " + owaDir.getAbsolutePath() + "...\n"); @@ -66,7 +73,7 @@ private File prepareOwaDir() { File owaDir = new File(System.getProperty("user.dir")); boolean pathCorrect = false; do { - String owaDirValue = wizard.promptForValueIfMissingWithDefault("Please specify a directory for OWA project (a relative or absolute path)", null, null, null); + String owaDirValue = wizard.promptForValueIfMissingWithDefault("Please specify a directory for OWA project (a relative or absolute path)", owaDir.getName(), null, null); File owaDirPath = new File(owaDirValue); if (owaDirPath.isAbsolute()) { owaDir = owaDirPath; diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/SDKConstants.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/SDKConstants.java index db005702..44199c93 100644 --- a/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/SDKConstants.java +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/SDKConstants.java @@ -27,6 +27,10 @@ public class SDKConstants { public static final String PLUGIN_DEPENDENCIES_GROUP_ID = "org.apache.maven.plugins"; public static final String PLUGIN_DEPENDENCIES_ARTIFACT_ID = "maven-dependency-plugin"; public static final String PLUGIN_DEPENDENCIES_VERSION = "2.8"; + // archetype plugin + public static final String PLUGIN_ARCHETYPE_GROUP_ID = "org.apache.maven.plugins"; + public static final String PLUGIN_ARCHETYPE_ARTIFACT_ID = "maven-archetype-plugin"; + public static final String PLUGIN_ARCHETYPE_VERSION = "2.4"; //docker plugin public static final String PLUGIN_DOCKER_ARTIFACT_ID = "openmrs-sdk-docker-maven-plugin"; // release plugin diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/XmlHelper.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/XmlHelper.java new file mode 100644 index 00000000..76dccc68 --- /dev/null +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/XmlHelper.java @@ -0,0 +1,96 @@ +package org.openmrs.maven.plugins.utility; + +import org.apache.commons.io.IOUtils; +import org.apache.maven.plugin.MojoExecutionException; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.dom4j.io.XMLWriter; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringWriter; + +public class XmlHelper { + + public void modifyXml(File targetfile, String modificationsPath) throws MojoExecutionException { + + StringWriter writer = new StringWriter(); + + try ( + InputStream modificationsStream = getClass().getClassLoader().getResourceAsStream(modificationsPath) + ){ + Document targetDocument = getDocument(targetfile); + Document modificationsDocument = getDocument(modificationsStream); + + Element targetRoot = targetDocument.getRootElement(); + Element modificationsRoot = modificationsDocument.getRootElement(); + + if(!targetRoot.getName().equals(modificationsRoot.getName())){ + throw new IllegalArgumentException( + "Target file and modifications file have differing root elements - target:"+targetRoot.getName()+", root: "+modificationsRoot.getName() + ); + } + + applyModifications(modificationsRoot, targetRoot); + + XMLWriter xmlWriter = new XMLWriter( writer ); + xmlWriter.write( targetDocument ); + + FileUtils.fileWrite(targetfile, writer.toString()); + + } catch (IOException | DocumentException e) { + throw new MojoExecutionException("Failed to create owa submodule", e); + } + } + + protected void applyModifications(Element modificationsRoot, Element targetRoot) { + for(Object object : modificationsRoot.elements()){ + if(object instanceof Element){ + Element modificationElement = (Element) object; + String copy = modificationElement.attributeValue("sdk-copy"); + if("true".equals(copy)){ + addModification(targetRoot, modificationElement); + } else { + Element targetElement = targetRoot.element(modificationElement.getName()); + if(targetElement == null) { + targetElement = targetRoot.addElement(modificationElement.getName()); + } + applyModifications(modificationElement, targetElement); + } + } + } + } + + protected void addChildren(Element modificationsRoot, Element targetRoot){ + for(Object object : modificationsRoot.elements()){ + if(object instanceof Element){ + addModification(targetRoot, (Element) object); + } + } + } + + protected void addModification(Element targetRoot, Element modificationElement) { + Element targetElement = targetRoot.addElement(modificationElement.getName()); + targetElement.setText(modificationElement.getText()); + //add children values + addChildren(modificationElement, targetElement); + } + + protected Document getDocument(File file) throws IOException, DocumentException { + return getDocument(new FileInputStream(file)); + } + + protected Document getDocument(InputStream stream) throws IOException, DocumentException { + Reader targetReader = ReaderFactory.newXmlReader(stream); + Document doc = new SAXReader().read(targetReader); + IOUtils.closeQuietly(stream); + return doc; + } +} diff --git a/maven-plugin/src/main/resources/archetype-submodule-owa/config.xml b/maven-plugin/src/main/resources/archetype-submodule-owa/config.xml new file mode 100644 index 00000000..57fb6342 --- /dev/null +++ b/maven-plugin/src/main/resources/archetype-submodule-owa/config.xml @@ -0,0 +1,6 @@ + + + + org.openmrs.module.owa + + \ No newline at end of file diff --git a/maven-plugin/src/main/resources/archetype-submodule-owa/omod.pom.xml b/maven-plugin/src/main/resources/archetype-submodule-owa/omod.pom.xml new file mode 100644 index 00000000..9b9fe1b0 --- /dev/null +++ b/maven-plugin/src/main/resources/archetype-submodule-owa/omod.pom.xml @@ -0,0 +1,41 @@ + + + + + ${project.parent.groupId} + ${project.parent.artifactId}-owa + ${project.parent.version} + zip + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + include-owa + process-resources + + copy + + + + + ${project.parent.groupId} + ${project.parent.artifactId}-owa + ${project.parent.version} + zip + ${project.parent.artifactId}.owa + + + ${project.build.directory}/classes/web/module/owas + + + + + + + \ No newline at end of file diff --git a/maven-plugin/src/main/resources/archetype-submodule-owa/owa.gitignore b/maven-plugin/src/main/resources/archetype-submodule-owa/owa.gitignore new file mode 100644 index 00000000..089485fb --- /dev/null +++ b/maven-plugin/src/main/resources/archetype-submodule-owa/owa.gitignore @@ -0,0 +1,8 @@ +### npm owa ### +owa/node_modules +owa/dist +owa/config.json +owa/node +owa/*.zip +owa/etc +########### \ No newline at end of file diff --git a/maven-plugin/src/test/java/org/openmrs/maven/plugins/utility/XmlHelperTest.java b/maven-plugin/src/test/java/org/openmrs/maven/plugins/utility/XmlHelperTest.java new file mode 100644 index 00000000..7946883b --- /dev/null +++ b/maven-plugin/src/test/java/org/openmrs/maven/plugins/utility/XmlHelperTest.java @@ -0,0 +1,66 @@ +package org.openmrs.maven.plugins.utility; + +import org.dom4j.Document; +import org.dom4j.DocumentFactory; +import org.dom4j.Element; +import org.dom4j.tree.DefaultElement; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNull.notNullValue; + +public class XmlHelperTest { + + private XmlHelper helper = new XmlHelper(); + + @Test + public void testApplyChanges(){ + Element modificationsRoot = new DefaultElement("project"); + Element targetRoot = new DefaultElement("project"); + + Document target = DocumentFactory.getInstance().createDocument(targetRoot); + Document modifications = DocumentFactory.getInstance().createDocument(modificationsRoot); + + Element targetDependencies = target.getRootElement().addElement("dependencies"); + + Element dependency = modifications.getRootElement() + .addElement("dependencies") + .addElement("dependency").addAttribute("sdk-copy", "true"); + + dependency.addElement("groupId").setText("org.openmrs.module"); + dependency.addElement("artifactId").setText("metadtamapiing"); + dependency.addElement("version").setText("2345"); + + assertThat(target.getRootElement().element("dependencies").element("dependency"), is(nullValue())); + + helper.applyModifications(modifications.getRootElement(), target.getRootElement()); + + assertThat(target.getRootElement().element("dependencies").element("dependency"), is(notNullValue())); + assertThat(target.getRootElement().element("dependencies").element("dependency").element("groupId").getText(), is("org.openmrs.module")); + } + + @Test + public void testApplyChanges2(){ + Element modificationsRoot = new DefaultElement("project"); + Element targetRoot = new DefaultElement("project"); + + Document target = DocumentFactory.getInstance().createDocument(targetRoot); + Document modifications = DocumentFactory.getInstance().createDocument(modificationsRoot); + + Element dependency = modifications.getRootElement() + .addElement("dependencies") + .addElement("dependency").addAttribute("sdk-copy", "true"); + + dependency.addElement("groupId").setText("org.openmrs.module"); + dependency.addElement("artifactId").setText("metadtamapiing"); + dependency.addElement("version").setText("2345"); + + assertThat(target.getRootElement().element("dependencies"), is(nullValue())); + + helper.applyModifications(modifications.getRootElement(), target.getRootElement()); + + assertThat(target.getRootElement().element("dependencies").element("dependency"), is(notNullValue())); + } +} diff --git a/pom.xml b/pom.xml index 67df9b10..7c3604ea 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ archetype-module-refapp archetype-module-platform + archetype-submodule-owa docker-maven-plugin maven-plugin integration-tests