Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add validations for user inputs #57

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,9 @@ public enum TokenPosition {

public static final String LICENSE_YEAR = "2023";
public static final String DATA_TYPE_EXTENSION = "Extension";

public class PrintStrings {
public static final String OVERWRITING_EXISTING_PACKAGE = "[INFO] Overwriting the existing package.";
public static final String INVALID_INPUT = "[ERROR] Invalid input. Exiting the tool.";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,20 @@ public List<DependencyConfig> getDependencyConfigList() {
}

public void setOrg(String org) {
this.org = org;
// org name of a ballerina package can have only alhpa-numeric chars and '_'
// it cannot have consecutive '_'
// and it cannot start/end with '_'
String normalizedOrg = org.replaceAll("[^a-zA-Z0-9_]", "_");
if (normalizedOrg.contains("__")) {
normalizedOrg = normalizedOrg.replaceAll("_{2,}", "_");
}
if (normalizedOrg.startsWith("_")) {
normalizedOrg = ProjectUtils.removeFirstChar(normalizedOrg);
}
if (normalizedOrg.endsWith("_")) {
normalizedOrg = ProjectUtils.removeLastChar(normalizedOrg);
}
this.org = normalizedOrg;
}

public void setName(String name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ public void generate(ToolContext toolContext, Map<String, Object> generatorPrope
if ("n".equalsIgnoreCase(input)) {
System.exit(0);
} else if ("y".equalsIgnoreCase(input)) {
System.out.println("Overwriting the existing package.");
System.out.println(ToolConstants.PrintStrings.OVERWRITING_EXISTING_PACKAGE);
} else {
System.out.println("Invalid input. Exiting the tool.");
System.out.println(ToolConstants.PrintStrings.INVALID_INPUT);
System.exit(0);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,13 @@ public class BallerinaProjectConstants {
public static final String SERVICE_PACKAGE_IMPORT_SUFFIX = "ballerinax/health.fhir";
public static final String INTERNATIONAL_PACKAGE_IMPORT_SUFFIX = "ballerinax/health.fhir.r4.international401";

public class PrintStrings {

public static final String OVERWRITING_EXISTING_TEMPLATES = "[INFO] Overwriting the existing templates.";
public static final String TEMPLATES_FOR_ALL_PROFILES = "[INFO] Generating templates for all FHIR profiles...";

public static final String INVALID_PROFILE = "[WARN] Invalid FHIR profile: ";

public static final String INVALID_INPUT = "[ERROR] Invalid input. Exiting the tool.";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.wso2.healthcare.fhir.codegen.ballerina.project.tool.model.SearchParam;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -117,11 +118,18 @@ private void populateIGs(ToolContext toolContext) {
private void populateBalService() {
for (Map.Entry<String, FHIRImplementationGuide> entry : igMap.entrySet()) {
String igName = entry.getKey();
for (Map.Entry<String, FHIRResourceDef> definitionEntry : entry.getValue().getResources().entrySet()) {
if (definitionEntry.getValue().getDefinition().getKind().toCode().equalsIgnoreCase("RESOURCE")) {
validateAndAddFHIRResource(definitionEntry.getValue().getDefinition(), igName);
// extract structure definitions of resource types
Map<String, FHIRResourceDef> resourceDefMap = new HashMap<>();
entry.getValue().getResources().forEach((k, resourceDef) -> {
if (resourceDef.getDefinition().getKind().toCode().equalsIgnoreCase("RESOURCE")) {
resourceDefMap.put(k, resourceDef);
}
}
});
// filter structure definitions based on included/excluded
List<StructureDefinition> structureDefinitions = retrieveStructureDef(igName, resourceDefMap);
structureDefinitions.forEach(definition -> {
addResourceProfile(definition, definition.getType(), definition.getName(), definition.getUrl(), igName);
});
//adding Search parameters
for (Map.Entry<String, FHIRSearchParamDef> parameter : entry.getValue().getSearchParameters().entrySet()) {
List<CodeType> baseResources = parameter.getValue().getSearchParameter().getBase();
Expand All @@ -137,6 +145,58 @@ private void populateBalService() {
}
}

private List<StructureDefinition> retrieveStructureDef(String igName, Map<String, FHIRResourceDef> resourceDefMap) {
List<StructureDefinition> structureDefinitions = new ArrayList<>();
List<String> includedProfiles =
ballerinaProjectToolConfig.getIncludedIGConfigs().get(igName).getIncludedProfiles();
List<String> excludedProfiles =
ballerinaProjectToolConfig.getIncludedIGConfigs().get(igName).getExcludedProfiles();
if (!includedProfiles.isEmpty()) {
for (String profile : includedProfiles) {
if (resourceDefMap.containsKey(profile)) {
structureDefinitions.add(resourceDefMap.get(profile).getDefinition());
} else {
// invalid url
System.out.println(BallerinaProjectConstants.PrintStrings.INVALID_PROFILE + profile);
}
}
if (structureDefinitions.isEmpty()) {
// nothing included
// generate template for all the profiles
System.out.println(BallerinaProjectConstants.PrintStrings.TEMPLATES_FOR_ALL_PROFILES);
resourceDefMap.forEach((k, resourceDef) -> {
structureDefinitions.add(resourceDef.getDefinition());
});
}
return structureDefinitions;
}
if (!excludedProfiles.isEmpty()) {
Map<String, FHIRResourceDef> resourceDefMapCopy = new HashMap<>(resourceDefMap);
for (String profile : excludedProfiles) {
if (resourceDefMapCopy.containsKey(profile)) {
resourceDefMapCopy.remove(profile);
} else {
// invalid url
System.out.println(BallerinaProjectConstants.PrintStrings.INVALID_PROFILE + profile);
}
}
resourceDefMapCopy.forEach((k, resourceDef) -> {
structureDefinitions.add(resourceDef.getDefinition());
});
if (resourceDefMap.size() == resourceDefMapCopy.size()) {
System.out.println(BallerinaProjectConstants.PrintStrings.TEMPLATES_FOR_ALL_PROFILES);
}
return structureDefinitions;
}
// nothing included or excluded
// generate templates for all the profiles
System.out.println(BallerinaProjectConstants.PrintStrings.TEMPLATES_FOR_ALL_PROFILES);
resourceDefMap.forEach((k, v) -> {
structureDefinitions.add(v.getDefinition());
});
return structureDefinitions;
}

private SearchParam getSearchParam(Map.Entry<String, FHIRSearchParamDef> parameter, String apiName) {
SearchParam param = new SearchParam(parameter.getValue().getSearchParameter().getName(),
parameter.getValue().getSearchParameter().getCode());
Expand All @@ -150,31 +210,6 @@ private SearchParam getSearchParam(Map.Entry<String, FHIRSearchParamDef> paramet
return param;
}

/**
* Validate Ballerina service based on include-exclude configs.
*
* @param structureDefinition FHIR StructureDefinition
* @param igName IG name
*/
public void validateAndAddFHIRResource(StructureDefinition structureDefinition, String igName) {

String resourceType = structureDefinition.getType();
String profile = structureDefinition.getName();
String url = structureDefinition.getUrl();

if (ballerinaProjectToolConfig.getIncludedIGConfigs().get(igName).getIncludedProfiles().isEmpty()) {
//add all resources of the IG except ones listed in excluded list
if (!ballerinaProjectToolConfig.getIncludedIGConfigs().get(igName).getExcludedProfiles().contains(url)) {
addResourceProfile(structureDefinition, resourceType, profile, url, igName);
}
} else {
//add resources listed in included list. Neglect excluded list
if (ballerinaProjectToolConfig.getIncludedIGConfigs().get(igName).getIncludedProfiles().contains(url)) {
addResourceProfile(structureDefinition, resourceType, profile, url, igName);
}
}
}

/**
* Adding Ballerina service model to a common map.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,20 @@ public List<String> getKeywords() {
}

public void setOrg(String org) {
this.org = org;
// org name of a ballerina package can have only alhpa-numeric chars and '_'
// it cannot have consecutive '_'
// and it cannot start/end with '_'
String normalizedOrg = org.replaceAll("[^a-zA-Z0-9_]", "_");
if (normalizedOrg.contains("__")) {
normalizedOrg = normalizedOrg.replaceAll("_{2,}", "_");
}
if (normalizedOrg.startsWith("_")) {
normalizedOrg = normalizedOrg.substring(1);
}
if (normalizedOrg.endsWith("_")) {
normalizedOrg = normalizedOrg.substring(0, normalizedOrg.length() - 1);
}
this.org = normalizedOrg;
}

public void setNamePrefix(String namePrefix) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ public void generate(ToolContext toolContext, Map<String, Object> generatorPrope
if ("n".equalsIgnoreCase(input)) {
System.exit(0);
} else if ("y".equalsIgnoreCase(input)) {
System.out.println("Overwriting the existing templates.");
System.out.println(BallerinaProjectConstants.PrintStrings.OVERWRITING_EXISTING_TEMPLATES);
} else {
System.out.println("Invalid input. Exiting the tool.");
System.out.println(BallerinaProjectConstants.PrintStrings.INVALID_INPUT);
System.exit(0);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,11 @@ private void printHelpTextAsStream() {
}
return;
} catch (IOException e) {
printStream.println("Helper text is not available.");
printStream.println(HealthCmdConstants.PrintStrings.HELP_NOT_AVAILABLE);
HealthCmdUtils.throwLauncherException(e);
}
}
printStream.println("An Error occurred internally while fetching the Help text.");
printStream.println(HealthCmdConstants.PrintStrings.HELP_ERROR);
HealthCmdUtils.exitError(true);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,27 @@ public class HealthCmdConstants {
public static final String CMD_DEFAULT_IG_NAME = "healthcare.fhir";
public static final String CMD_DEFAULT_ORG_NAME = "healthcare";

public class PrintStrings {

public static final String HELP_FOR_MORE_INFO = "[INFO] Try bal health --help for more information.";
public static final String TEMPLATE_GEN_SUCCESS = "[INFO] Ballerina FHIR API templates generation completed " +
"successfully. Generated templates can be found at: ";
public static final String PKG_GEN_SUCCESS = "[INFO] Ballerina FHIR package generation completed successfully."
+ " Generated package can be found at: ";

public static final String INVALID_SPEC_PATH = "[ERROR] Invalid specification path received for FHIR tool command.";
public static final String HELP_NOT_AVAILABLE = "[ERROR] Helper text is not available.";
public static final String HELP_ERROR = "[ERROR] An Error occurred internally while fetching the Help text.";
public static final String INVALID_NUM_OF_ARGS = "[ERROR] Invalid number of arguments received for FHIR tool "
+ "command.";
public static final String INVALID_MODE = "[ERROR] Invalid mode received for FHIR tool command.";
public static final String PKG_NAME_REQUIRED = "[ERROR] Package name [--package-name] is required for package "
+ "generation.";
public static final String DEPENDENT_REQUIRED = "[ERROR] Dependent package [--dependent-package] is required "
+ "for template generation.";
public static final String DEPENDENT_INCORRECT = "[ERROR] Format of the dependent package is incorrect.";
public static final String INCLUDED_EXCLUDED_TOGETHER = "[ERROR] Both --included-profile and "
+ "--excluded-profile cannot be used together.";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@

package io.ballerina.health.cmd.fhir;

import com.google.gson.JsonElement;
import io.ballerina.cli.BLauncherCmd;
import io.ballerina.cli.launcher.BLauncherException;
import io.ballerina.health.cmd.core.exception.BallerinaHealthException;
import io.ballerina.health.cmd.core.utils.HealthCmdConstants;
import io.ballerina.health.cmd.core.utils.HealthCmdUtils;
import io.ballerina.health.cmd.core.utils.JsonTypeConverter;
import io.ballerina.health.cmd.handler.Handler;
import io.ballerina.health.cmd.handler.HandlerFactory;
import picocli.CommandLine;
Expand All @@ -36,7 +34,6 @@
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
Expand All @@ -50,6 +47,7 @@
@CommandLine.Command(name = "fhir", description = "Generates Ballerina service/client for FHIR contract " +
"for Ballerina service.")
public class FhirSubCmd implements BLauncherCmd {

private final PrintStream printStream;
private final boolean exitWhenFinish;
private final String toolName = "fhir";
Expand Down Expand Up @@ -124,53 +122,59 @@ public void execute() {
}
return;
} catch (IOException e) {
printStream.println("Helper text is not available.");
printStream.println(HealthCmdConstants.PrintStrings.HELP_NOT_AVAILABLE);
HealthCmdUtils.throwLauncherException(e);
}
}
printStream.println("An Error occurred internally while fetching the Help text.");
printStream.println(HealthCmdConstants.PrintStrings.HELP_ERROR);
HealthCmdUtils.exitError(exitWhenFinish);
}
if (argList == null || argList.isEmpty()) {
//at minimum arg count is 1 (spec path)
printStream.println("Invalid number of arguments received for FHIR tool command.");
printStream.println("Try bal health --help for more information.");
printStream.println(HealthCmdConstants.PrintStrings.INVALID_NUM_OF_ARGS);
printStream.println(HealthCmdConstants.PrintStrings.HELP_FOR_MORE_INFO);
HealthCmdUtils.exitError(exitWhenFinish);
}
if (mode == null || mode.isEmpty()) {
//mode is required param
printStream.println("Invalid mode received for FHIR tool command.");
printStream.println("Try bal health --help for more information.");
printStream.println(HealthCmdConstants.PrintStrings.INVALID_MODE);
printStream.println(HealthCmdConstants.PrintStrings.HELP_FOR_MORE_INFO);
HealthCmdUtils.exitError(exitWhenFinish);
}
if (CMD_MODE_PACKAGE.equals(mode) && (packageName == null || packageName.isEmpty())) {
// package name is a required param in package mode
printStream.println("Package name [--package-name] is required for package generation.");
printStream.println("Try bal health --help for more information.");
printStream.println(HealthCmdConstants.PrintStrings.PKG_NAME_REQUIRED);
printStream.println(HealthCmdConstants.PrintStrings.HELP_FOR_MORE_INFO);
HealthCmdUtils.exitError(exitWhenFinish);
}
if (CMD_MODE_TEMPLATE.equals(mode) && (dependentPackage == null || dependentPackage.isEmpty())) {
// dependent package is a required param in template mode
printStream.println("Dependent package [--dependent-package] is required for template generation.");
printStream.println("Try bal health --help for more information.");
printStream.println(HealthCmdConstants.PrintStrings.DEPENDENT_REQUIRED);
printStream.println(HealthCmdConstants.PrintStrings.HELP_FOR_MORE_INFO);
HealthCmdUtils.exitError(exitWhenFinish);
}
if (dependentPackage != null && !dependentPackage.isEmpty()) {
if (!dependentPackage.matches("^[^/]+/[^/]+$")) {
printStream.println("Format of the dependent package is incorrect.");
printStream.println("Try bal health --help for more information.");
// regex matching ballerinax/health.fhir.r4
if (!dependentPackage.matches("^(?!.*__)[a-zA-Z0-9][a-zA-Z0-9_]+[a-zA-Z0-9]/[a-zA-Z0-9][a-zA-Z0-9._]+[a-zA-Z0-9]$")) {
printStream.println(HealthCmdConstants.PrintStrings.DEPENDENT_INCORRECT);
printStream.println(HealthCmdConstants.PrintStrings.HELP_FOR_MORE_INFO);
HealthCmdUtils.exitError(exitWhenFinish);
}
}
if (includedProfiles != null && excludedProfiles != null) {
printStream.println(HealthCmdConstants.PrintStrings.INCLUDED_EXCLUDED_TOGETHER);
printStream.println(HealthCmdConstants.PrintStrings.HELP_FOR_MORE_INFO);
HealthCmdUtils.exitError(exitWhenFinish);
}
if (this.engageSubCommand(argList)) {
if (CMD_MODE_TEMPLATE.equals(mode)) {
printStream.println("Ballerina FHIR API templates generation completed successfully. Generated templates can be found at " + targetOutputPath);
printStream.println(HealthCmdConstants.PrintStrings.TEMPLATE_GEN_SUCCESS + targetOutputPath);
} else {
printStream.println("Ballerina FHIR package generation completed successfully. Generated package can be found at " + targetOutputPath);
printStream.println(HealthCmdConstants.PrintStrings.PKG_GEN_SUCCESS + targetOutputPath);
}
} else {
printStream.println("Invalid mode received for FHIR tool command.");
printStream.println("Try bal health --help for more information.");
printStream.println(HealthCmdConstants.PrintStrings.INVALID_MODE);
printStream.println(HealthCmdConstants.PrintStrings.HELP_FOR_MORE_INFO);
}

HealthCmdUtils.exitError(exitWhenFinish);
Expand Down Expand Up @@ -210,7 +214,7 @@ public boolean engageSubCommand(List<String> argList) {
try {
specificationPath = HealthCmdUtils.validateAndSetSpecificationPath(argList.get(argList.size() - 1), executionPath.toString());
} catch (BallerinaHealthException e) {
printStream.println("Invalid specification path received for FHIR tool command.");
printStream.println(HealthCmdConstants.PrintStrings.INVALID_SPEC_PATH);
throw new BLauncherException();
}
Handler toolHandler = null;
Expand Down
Loading
Loading