Skip to content

Commit

Permalink
O3-3511: Added plugin to Validate and Assemble content package (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
nravilla authored Aug 12, 2024
1 parent 5b518e4 commit 811b90c
Show file tree
Hide file tree
Showing 5 changed files with 452 additions and 209 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@

package org.openmrs.maven.plugins.packager.config;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.io.FileInputStream;
import java.util.Properties;

import org.apache.maven.plugin.MojoExecutionException;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class ValidateContentPackageMojoTest {

@InjectMocks
private ValidateContentPackageMojo mojo = new ValidateContentPackageMojo();

@Mock
private FileInputStream mockFileInputStream;

@Mock
private Properties mockProperties;

@Rule
public ExpectedException exceptionRule = ExpectedException.none();

@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}

@Test
public void executeValidContentPropertiesFileWithoutErrors() throws Exception {
String validPropertiesFile = "src/test/resources/config-test-child/valid-content.properties";
mojo.sourceFile = validPropertiesFile;
mojo.execute();
//no exception is thrown
}

@Test(expected = MojoExecutionException.class)
public void executeInvalidContentPropertiesFormatWithException() throws Exception {
String invalidPropertiesFile = "src/test/resources/config-test-child/invalid-content.properties";
mojo.sourceFile = invalidPropertiesFile;
mojo.execute();
//exception is thrown
}

@Test
public void testInValidVersions() {
assertFalse(mojo.isValid("latest"));
assertFalse(mojo.isValid("next"));

}

public void testValidVersionRanges() {
assertTrue(mojo.isValid("1.0"));
assertTrue(mojo.isValid("0.13.0"));
assertTrue(mojo.isValid("0.13.0"));
assertTrue(mojo.isValid("^2.13.0"));
assertTrue(mojo.isValid("~0.13.0"));
assertTrue(mojo.isValid(">0.13.0"));
assertTrue(mojo.isValid("<3.0.0"));
assertTrue(mojo.isValid(">=3.0.0"));
assertTrue(mojo.isValid(">=1.2.3"));
assertTrue(mojo.isValid(">=1.2"));
assertTrue(mojo.isValid("<=3.0.0"));
assertTrue(mojo.isValid("1.0.0 - 1.10.10"));
assertTrue(mojo.isValid("<2.1.0 || >2.6.0"));
assertTrue(mojo.isValid(">=1.0.0-SNAPSHOT"));
assertTrue(mojo.isValid(">=1.0.0-pre.1"));
//some edge cases
assertFalse(mojo.isValid("1.0.0-1234-"));
assertFalse(mojo.isValid("abc"));
assertFalse(mojo.isValid("1..0"));
}

@Test
public void validNPMVersion() {
assertTrue(mojo.isValid("1.0.01"));
assertTrue(mojo.isValid("1.0.0-alpha@"));
assertTrue(mojo.isValid("1.0.0.0"));
}

@Test
public void testComplexRanges() {
assertTrue(mojo.isValid(">=1.0.0 <2.0.0"));
assertTrue(mojo.isValid("1.1.1 || 1.2.3 - 2.0.0"));
assertTrue(mojo.isValid(">=1.0.0 - <2.0.0"));
assertTrue(mojo.isValid("~2.1.5 || ~2.4.2 || >= 2.5-SNAPSHOT"));
assertTrue(mojo.isValid("1.1.1 || 1.2.3 ! 2.0.0"));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# content.properties - Metadata for the content package and its dependencies

# The name of this content package
name=openmrs-content-hiv

# The version of this content package (must follow SemVer)
version=latest

# Dependencies (specified in a format similar to distro.properties)

# OMOD modules with their SemVer range
omod.fhir2=>=1.0.0-SNAPSHOT
omod.reporting=^2.0.0

# SPA modules with their SemVer range
spa.frontendModules.@openmrs/esm-login-app=^3.0.0
spa.frontendModules.@openmrs/esm-patient-chart=~1.0.0

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# content.properties - Metadata for the content package and its dependencies

# The name of this content package
name=openmrs-content-hiv

# The version of this content package (must follow SemVer)
version=1.0.0-SNAPSHOT

# Dependencies (specified in a format similar to distro.properties)

# OMOD modules with their SemVer range

omod.reporting=>=2.0.1

# SPA modules with their SemVer range
spa.frontendModules.@openmrs/esm-login-app=1.20.2 - 12.3.0
spa.frontendModules.@openmrs/esm-patient-chart=^1.0.0

Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, you can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark, and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.maven.plugins.packager.config;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.semver4j.Semver;
import org.semver4j.SemverException;

/**
* Mojo to validate content properties file - ensures the properties are only within allowed ranges.
* Values like "latest" and "next" are not permitted.
*/
@Mojo(name = "validate-content-package")
public class ValidateContentPackageMojo extends AbstractMojo {

// List of terms that are not valid versions
private static final String[] INVALID_TERMS = { "latest", "next" };

/**
* The full path to the content.properties file, including the filename. This file is similar to
* distro.properties used in the validation process. For example:
* "{project.basedir}/content.properties".
*/
@Parameter(property = "sourceFile")
protected String sourceFile;

/**
* Executes the property validation.
*
* @throws MojoExecutionException if an error occurs during validation
*/
@Override
public void execute() throws MojoExecutionException {
validateProperties();
}

/**
* Validates the properties in the given file.
*
* @throws MojoExecutionException if an error occurs while reading the file
*/
private void validateProperties() throws MojoExecutionException {
if (sourceFile == null) {
throw new MojoExecutionException(
"sourceFile is missing. A valid path and file for content.properties are required for this plugin.");
}
try (InputStream inputStream = new FileInputStream(sourceFile)) {
Properties properties = new Properties();
properties.load(inputStream);

if (!properties.containsKey("name") || !properties.containsKey("version")) {
throw new MojoExecutionException("The properties file must contain both 'name' and 'version' keys.");
}

for (String key : properties.stringPropertyNames()) {
String value = properties.getProperty(key);

if (key.startsWith("omod") || key.startsWith("owa") || key.startsWith("spa.frontend")
|| "version".equalsIgnoreCase(key)) {
if (!isValid(value)) {
throw new MojoExecutionException("Invalid SemVer format for key: " + key + ", value: " + value);
}
}
}
}
catch (Exception e) {
throw new MojoExecutionException("Could not validate configuration file '" + sourceFile + "': " + e.getMessage(),
e);
}
}

/**
* Validates whether a given value is a valid SemVer expression. If the range expression
* satisfies(true or false), it is considered valid. If an exception occurs, the range
* expression is invalid.
*
* @param versionOrRange the value to validate
* @return true if the value is a valid SemVer expression or range, false otherwise
*/
protected boolean isValid(String versionOrRange) {

for (String term : INVALID_TERMS) {
if (term.equalsIgnoreCase(versionOrRange)) {
return false;
}
}

try {
Semver semver = new Semver("0.0.0");
semver.satisfies(versionOrRange);
return true;
}
catch (SemverException e) {
return false;
}
}
}
Loading

0 comments on commit 811b90c

Please sign in to comment.