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 1 commit
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 @@ -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 @@ -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("Invalid fhir profile to include: " + profile);
ThishaniLucas marked this conversation as resolved.
Show resolved Hide resolved
}
}
if (structureDefinitions.isEmpty()) {
// nothing included
// generate template for all the profiles
System.out.println("Generating templates for all FHIR 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("Invalid fhir profile to exclude: " + profile);
}
}
resourceDefMapCopy.forEach((k, resourceDef) -> {
structureDefinitions.add(resourceDef.getDefinition());
});
if (resourceDefMap.size() == resourceDefMapCopy.size()) {
System.out.println("Generating templates for all FHIR profiles...");
}
return structureDefinitions;
}
// nothing included or excluded
// generate templates for all the profiles
System.out.println("Generating templates for all FHIR 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 @@ -156,12 +156,18 @@ public void execute() {
HealthCmdUtils.exitError(exitWhenFinish);
}
if (dependentPackage != null && !dependentPackage.isEmpty()) {
if (!dependentPackage.matches("^[^/]+/[^/]+$")) {
// 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("Format of the dependent package is incorrect.");
printStream.println("Try bal health --help for more information.");
HealthCmdUtils.exitError(exitWhenFinish);
}
}
if (includedProfiles != null && excludedProfiles != null) {
printStream.println("Both --included-profile and --excluded-profile cannot be used together.");
printStream.println("Try bal health --help for more information.");
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);
Expand Down
36 changes: 22 additions & 14 deletions native/health-cli/src/main/resources/ballerina-health.help
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ DESCRIPTION
from a given health specification (eg.:FHIR implementation guides) files.

The generated Ballerina sources will be written into the provided
output location. Make sure to add the directory path which contains FHIR specification,
as the last argument.
output location. Make sure to add the directory path which contains
FHIR specification, as the last argument.

You can download FHIR specification files from the respective
Implementation Guide's official website. (Published list of
Expand All @@ -36,36 +36,44 @@ COMMANDS

OPTIONS
-m, --mode <mode-type>
Mode can be 'package' or 'template'. If the mode is set to 'package',
a Ballerina package will be generated including all the records
and types. If the mode is set to ‘template’, tool will generate
Ballerina templates for each FHIR resource definition available
in the specified path. This is a MANDATORY input for fhir command.
Mode can be 'package' or 'template'. If the mode is set to
'package', a Ballerina package will be generated including all the
records and types. If the mode is set to ‘template’, tool will
generate Ballerina templates for each FHIR resource definition
available in the specified path. This is a MANDATORY input for fhir
command.

--package-name <name-of-package>
Only applicable in ‘package’ mode. Name of the Ballerina package
to be generated. This is a MANDATORY input in ‘package’ mode.
Refer https://ballerina.io/learn/package-references/#the-name-field

--dependent-package <qualified-ballerina-package-name>
Only applicable in ‘template’ mode. Fully qualified name of the
published Ballerina package containing IG resources [eg: <org>/<package>].
This option can be used to generate templates specifically for the
resources in the given IG. The package name part of this value
will be added as a prefix to the template name.
This is a MANDATORY input in ‘template’ mode.
published Ballerina package containing IG resources
[eg: <org>/<package>]. This option can be used to generate
templates specifically for the resources in the given IG. The
package name part of this value will be added as a prefix to the
template name. This is a MANDATORY input in ‘template’ mode.

-o, --output <output>
Location of the generated Ballerina artifacts. If this
path is not specified, the output will be written to
the same directory from which the command is run.

--org-name <org-name-of-the-package>
Organization name of the Ballerina package/template to be generated.
Refer https://ballerina.io/learn/package-references/#the-org-field

--package-version <version-of-the-package>
Version of the Ballerina package/template to be generated.
Refer https://semver.org/

--included-profile <profiles-to-included>
Only applicable in ‘template’ mode. If only a specific profile/s
needs to be generated as templates, specify the profile URL
as the value of this parameter. This argument can be used more than once.
needs to be generated as templates, specify the profile URL as the
value of this parameter. This argument can be used more than once.

--excluded-profile <profiles-to-excluded>
Only applicable in ‘template’ mode. If only a specific profile/s
needs to be skipped when generating templates, specify the
Expand Down
Loading