diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 7c785992..53a29ada 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -42,5 +42,9 @@ hamcrest-library test + + org.semver4j + semver4j + diff --git a/integration-tests/src/test/java/org/openmrs/maven/plugins/AddDependencyTest.java b/integration-tests/src/test/java/org/openmrs/maven/plugins/AddDependencyTest.java new file mode 100644 index 00000000..51dc4712 --- /dev/null +++ b/integration-tests/src/test/java/org/openmrs/maven/plugins/AddDependencyTest.java @@ -0,0 +1,137 @@ +package org.openmrs.maven.plugins; + +import org.apache.maven.plugin.MojoExecutionException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.openmrs.maven.plugins.model.DistroProperties; +import org.openmrs.maven.plugins.utility.DistroHelper; + +import java.io.File; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class AddDependencyTest extends AbstractSdkIntegrationTest { + + private String distroFile; + + private DistroProperties originalProperties; + + + @Before + public void setUp() { + distroFile = testDirectory + File.separator + "openmrs-distro.properties"; + originalProperties = DistroHelper.getDistroPropertiesFromFile(new File(distroFile)); + } + + @Test + public void shouldAddOmodDependency() throws Exception { + addTaskParam("distro", distroFile); + addTaskParam("type", "OMOD"); + addTaskParam("groupId", "org.openmrs.module"); + addTaskParam("artifactId", "webservices.rest"); + addTaskParam("version", "2.30.0"); + + executeTask("add"); + assertSuccess(); + + DistroProperties distroProperties = DistroHelper.getDistroPropertiesFromFile(new File(distroFile)); + assertNotNull(distroProperties); + assertTrue(distroProperties.getAllKeys().contains("omod.webservices.rest")); + assertEquals(distroProperties.getParam("omod.webservices.rest"), "2.30.0"); + } + + @Test + public void shouldAddSpaDependency() throws Exception { + addTaskParam("distro", distroFile); + addTaskParam("type", "SPA"); + addTaskParam("moduleName", "@openmrs/esm-system-admin-app"); + addTaskParam("version", "4.0.3"); + + executeTask("add"); + assertSuccess(); + + DistroProperties distroProperties = DistroHelper.getDistroPropertiesFromFile(new File(distroFile)); + assertNotNull(distroProperties); + + assertTrue(distroProperties.getAllKeys().contains("spa.frontendModules.@openmrs/esm-system-admin-app")); + assertEquals(distroProperties.getParam("spa.frontendModules.@openmrs/esm-system-admin-app"), "4.0.3"); + } + + @Test + public void shouldAddOwaDependency() throws Exception { + addTaskParam("distro", distroFile); + addTaskParam("type", "OWA"); + addTaskParam("artifactId", "sysadmin"); + addTaskParam("version", "1.2.0"); + + executeTask("add"); + assertSuccess(); + + DistroProperties distroProperties = DistroHelper.getDistroPropertiesFromFile(new File(distroFile)); + assertNotNull(distroProperties); + + assertTrue(distroProperties.getAllKeys().contains("owa.sysadmin")); + assertEquals(distroProperties.getParam("owa.sysadmin"), "1.2.0"); + } + + @Test + public void shouldAddWarDependency() throws Exception { + addTaskParam("distro", distroFile); + addTaskParam("type", "WAR"); + addTaskParam("version", "2.6.1"); + + executeTask("add"); + assertSuccess(); + + DistroProperties distroProperties = DistroHelper.getDistroPropertiesFromFile(new File(distroFile)); + assertNotNull(distroProperties); + + assertTrue(distroProperties.getAllKeys().contains("war.openmrs")); + assertEquals(distroProperties.getParam("war.openmrs"), "2.6.1"); + } + + @Test + public void shouldAddCustomDependencyIfTypeIsNotSpecified() throws Exception { + addTaskParam("distro", distroFile); + addTaskParam("property", "custom.property"); + addTaskParam("version", "1.2.3"); + + executeTask("add"); + assertSuccess(); + + DistroProperties distroProperties = DistroHelper.getDistroPropertiesFromFile(new File(distroFile)); + assertTrue(distroProperties.getAllKeys().contains("custom.property")); + assertEquals(distroProperties.getParam("custom.property"), "1.2.3"); + + } + + @Test + public void shouldOverrideIfPropertyAlreadyExists() throws Exception { + DistroProperties distroProperties = DistroHelper.getDistroPropertiesFromFile(new File(distroFile)); + assertNotNull(distroProperties); + assertTrue(distroProperties.getAllKeys().contains("omod.uiframework")); + + addTaskParam("distro", distroFile); + addTaskParam("type", "OMOD"); + addTaskParam("groupId", "org.openmrs.module"); + addTaskParam("artifactId", "uiframework"); + addTaskParam("version", "2.30.0"); + + executeTask("add"); + assertSuccess(); + + distroProperties = DistroHelper.getDistroPropertiesFromFile(new File(distroFile)); + assertNotNull(distroProperties); + + assertTrue(distroProperties.getAllKeys().contains("omod.uiframework")); + assertEquals(distroProperties.getParam("omod.uiframework"), "2.30.0"); + } + + @After + public void reset() throws MojoExecutionException { + originalProperties.saveTo(new File(distroFile).getParentFile()); + } +} diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/AddDependency.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/AddDependency.java new file mode 100644 index 00000000..f358efa8 --- /dev/null +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/AddDependency.java @@ -0,0 +1,157 @@ +package org.openmrs.maven.plugins; + +import org.apache.commons.lang.StringUtils; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.openmrs.maven.plugins.model.Artifact; +import org.openmrs.maven.plugins.model.DistroProperties; + +import org.openmrs.maven.plugins.utility.NpmVersionHelper; +import org.openmrs.maven.plugins.utility.SDKConstants; + +import java.io.File; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@Mojo(name = "add", requiresProject = false) +public class AddDependency extends AbstractTask { + + /** + * Path to the openmrs-distro.properties file to modify + */ + @Parameter(property = "distro") + private String distro; + + /** + * Type of the dependency to add (OMOD, SPA, OWA, WAR or Custom) + */ + @Parameter(property = "type") + private String type; + + /** + * Maven group id of the dependency + */ + @Parameter(property = "groupId") + private String groupId; + + /** + * Maven artifact id of the dependency + */ + @Parameter(property = "artifactId") + private String artifactId; + + /** + * Version of the dependency + */ + @Parameter(property = "version") + private String version; + + /** + * Name of the frontend module + */ + @Parameter(property = "moduleName") + private String moduleName; + + /** + * Name of the custom property + */ + @Parameter(property = "property") + private String property; + + private static final String DEPENDENCY_TYPE_PROMPT = "Enter the type of dependency you need to add"; + + private static final String OMOD_OPTION = "OMOD"; + private static final String SPA_OPTION = "SPA"; + private static final String OWA_OPTION = "OWA"; + private static final String WAR_OPTION = "WAR"; + private static final String CUSTOM_OPTION = "Custom"; + + + @Override + public void executeTask() throws MojoExecutionException, MojoFailureException { + if (distro == null) { + File userDir = new File(System.getProperty("user.dir")); + File distroFile = new File(userDir, DistroProperties.DISTRO_FILE_NAME); + if (distroFile.exists()) { + distro = distroFile.getAbsolutePath(); + } + } + + if (StringUtils.isBlank(type) && StringUtils.isBlank(property)) { + List dependencyTypes = new ArrayList<>(Arrays.asList(OMOD_OPTION, SPA_OPTION, OWA_OPTION, WAR_OPTION, CUSTOM_OPTION)); + type = wizard.promptForMissingValueWithOptions(DEPENDENCY_TYPE_PROMPT, null, null, dependencyTypes); + } + + if (StringUtils.isBlank(type) && StringUtils.isNotBlank(property)) { + type = CUSTOM_OPTION; + } + + DistroProperties distroProperties = null; + + if (StringUtils.isNotBlank(distro)) { + distroProperties = distroHelper.resolveDistroPropertiesForStringSpecifier(distro, versionsHelper); + } + + if (distroProperties == null) { + throw new MojoFailureException("Invalid distro properties"); + } + + switch (type.toUpperCase()) { + case OMOD_OPTION: + groupId = wizard.promptForValueIfMissingWithDefault(null, groupId, "groupId", Artifact.GROUP_MODULE); + artifactId = wizard.promptForValueIfMissing(artifactId, "artifactId"); + if (StringUtils.isBlank(version)) { + Artifact artifact = new Artifact(artifactId, "1.0", groupId); + List versions = versionsHelper.getSuggestedVersions(artifact, 5); + version = wizard.promptForMissingValueWithOptions("Enter the version", null, null, versions, + "Please specify the module version", null); + } + distroProperties.addProperty("omod." + artifactId, version); + break; + case SPA_OPTION: + moduleName = wizard.promptForValueIfMissing(moduleName, "frontend module name"); + if (StringUtils.isBlank(version)) { + List versions = new NpmVersionHelper().getPackageVersions(moduleName, 6);; + version = wizard.promptForMissingValueWithOptions("Enter the module version", null, null, versions, + "Please specify the SPA version", null); + } + distroProperties.addProperty("spa.frontendModules." + moduleName, version); + break; + case OWA_OPTION: + artifactId = wizard.promptForValueIfMissing(artifactId, "OWA name"); + Artifact artifact = new Artifact(artifactId, "1.0", Artifact.GROUP_OWA); + if (StringUtils.isBlank(version)) { + List suggestedVersions = versionsHelper.getSuggestedVersions(artifact, 6); + version = wizard + .promptForMissingValueWithOptions("Which version would you like to deploy?%s", null, "", suggestedVersions, + "Please specify OWA version", null); + } + distroProperties.addProperty("owa." + artifactId, version); + break; + case WAR_OPTION: + Artifact platformArtifact = new Artifact(SDKConstants.PLATFORM_ARTIFACT_ID, + SDKConstants.SETUP_DEFAULT_PLATFORM_VERSION, Artifact.GROUP_DISTRO); + if (StringUtils.isBlank(version)) { + version = wizard.promptForPlatformVersion(versionsHelper.getSuggestedVersions(platformArtifact, 5)); + } + distroProperties.addProperty("war.openmrs", version); + break; + default: + property = wizard.promptForValueIfMissing(property, "the property name (ex: omod.legacyui)"); + version = wizard.promptForValueIfMissing(version, "the property version"); + distroProperties.addProperty(property, version); + } + + if (StringUtils.isBlank(distro)) { + wizard.showMessage("No distro.properpties file is provided. Generating a new openmrs-distro.properties file."); + distro = Paths.get(System.getProperty("user.dir"), DistroProperties.DISTRO_FILE_NAME).toString(); + } + + distroProperties.saveTo(new File(distro).getParentFile()); + } + +} diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java index aa759d0e..4c887d34 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java @@ -1,5 +1,8 @@ package org.openmrs.maven.plugins.model; +import static org.openmrs.maven.plugins.utility.PropertiesUtils.loadPropertiesFromFile; +import static org.openmrs.maven.plugins.utility.PropertiesUtils.loadPropertiesFromResource; + import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.maven.plugin.MojoExecutionException; @@ -18,9 +21,6 @@ import java.util.Properties; import java.util.Set; -import static org.openmrs.maven.plugins.utility.PropertiesUtils.loadPropertiesFromFile; -import static org.openmrs.maven.plugins.utility.PropertiesUtils.loadPropertiesFromResource; - /** * */ @@ -292,6 +292,10 @@ public void addExclusion(String exclusion) { properties.setProperty("exclusions", exclusions + "," + exclusion); } + public void addProperty(String property, String value) throws MojoExecutionException { + properties.put(property, value); + } + private String getPlaceholderKey(String string){ int startIndex = string.indexOf("${")+2; int endIndex = string.indexOf("}", startIndex); diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java index f6f993aa..2ce46a7f 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java @@ -6,10 +6,16 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; public class NpmVersionHelper { @@ -44,6 +50,35 @@ public String getResolvedVersionFromNpmRegistry(PackageJson packageJson, String throw new RuntimeException("Error retrieving resolved version from NPM", e); } } + + public List getPackageVersions(String packageName, int limit) { + try { + ProcessBuilder processBuilder = new ProcessBuilder() + .command("npm", "view", packageName, "versions", "--dry-run", "--json").redirectErrorStream(true); + + Process process = processBuilder.start(); + + StringBuilder output = new StringBuilder(); + try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = bufferedReader.readLine()) != null) { + output.append(line); + } + } + + int exitCode = process.waitFor(); + if (exitCode != 0) { + return new ArrayList<>(); + } + JsonNode jsonNode = objectMapper.readTree(output.toString()); + + return StreamSupport.stream(jsonNode.spliterator(), false).map(JsonNode::asText) + .sorted(Collections.reverseOrder()).limit(limit).collect(Collectors.toList()); + + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } private static JsonNode getPackageMetadata(String versionRange, String packageName) throws IOException, InterruptedException { if (packageName == null || packageName.isEmpty()) {