Skip to content

Commit

Permalink
Fix profile dependency resolving issue
Browse files Browse the repository at this point in the history
  • Loading branch information
Thiyanwso2 committed Dec 19, 2024
1 parent cebafef commit d5519e0
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@ public class ToolConstants {
public static final String CONFIG_PACKAGE_REPOSITORY_TOML = "tools.config.package.repository";
public static final String CONFIG_BASE_PACKAGE = "basePackage";

public static final String CONFIG_PARENT_PACKAGE = "parentPackage";

public static final String CONFIG_PARENT_PACKAGE_TOML = "tools.config.package.parent_package";
public static final String CONFIG_BASE_PACKAGE_TOML = "tools.config.package.utils_package";
public static final String CONFIG_PACKAGE_DEPENDENCY = "dependencies";
public static final String CONFIG_PACKAGE_DEPENDENCY_TOML = "tools.config.package.dependency";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.wso2.healthcare.codegen.tool.framework.commons.model.TomlConfigType;
import org.wso2.healthcare.fhir.ballerina.packagegen.tool.ToolConstants;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -109,12 +110,19 @@ public void overrideConfig(String jsonPath, JsonElement value) {
case "packageConfig.org":
this.packageConfig.setOrg(value.getAsString());
break;
case "packageConfig.parentPackage":
this.packageConfig.setParentPackage(value.getAsString());
break;
case "packageConfig.name.append":
this.packageConfig.setName(packageConfig.getName() + "." + value.getAsString());
break;
case "packageConfig.profile.dependencies":
List<String> dependencyList = new ArrayList<>();
for (JsonElement jsonElement : value.getAsJsonArray()) {
String dependency = jsonElement.getAsString();
if (dependency != null && !dependency.isEmpty()) {
dependencyList.add(dependency);
}
}
this.packageConfig.setProfileDependencies(dependencyList);
break;
default:
LOG.warn("Invalid config path: " + jsonPath);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@
import org.wso2.healthcare.fhir.ballerina.packagegen.tool.ToolConstants;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Ballerina package level config.
Expand All @@ -40,7 +44,7 @@ public class PackageConfig {
private String repository;
private String basePackage;
private List<DependencyConfig> dependencyConfigList;
private String parentPackage;
private final Map<String, String> profileDependencies = new HashMap<>();

public PackageConfig(JsonObject packageConfigJson) {
this.org = packageConfigJson.getAsJsonPrimitive(ToolConstants.CONFIG_PACKAGE_ORG).getAsString();
Expand All @@ -50,7 +54,6 @@ public PackageConfig(JsonObject packageConfigJson) {
this.authors = packageConfigJson.getAsJsonArray(ToolConstants.CONFIG_PACKAGE_AUTHORS).getAsString();
this.repository = packageConfigJson.getAsJsonPrimitive(ToolConstants.CONFIG_PACKAGE_REPOSITORY).getAsString();
this.basePackage = packageConfigJson.getAsJsonPrimitive(ToolConstants.CONFIG_BASE_PACKAGE).getAsString();
this.parentPackage = packageConfigJson.getAsJsonPrimitive(ToolConstants.CONFIG_PARENT_PACKAGE).getAsString();
populateDependencies(packageConfigJson.getAsJsonArray(ToolConstants.CONFIG_PACKAGE_DEPENDENCY).getAsJsonArray());
}

Expand All @@ -62,7 +65,6 @@ public PackageConfig(TomlTable packageConfigToml) {
this.authors = packageConfigToml.getString(ToolConstants.CONFIG_PACKAGE_AUTHORS_TOML);
this.repository = packageConfigToml.getString(ToolConstants.CONFIG_PACKAGE_REPOSITORY_TOML);
this.basePackage = packageConfigToml.getString(ToolConstants.CONFIG_BASE_PACKAGE_TOML);
this.parentPackage = packageConfigToml.getString(ToolConstants.CONFIG_PARENT_PACKAGE_TOML);
populateDependencies(packageConfigToml.getArrayOrEmpty(ToolConstants.CONFIG_PACKAGE_DEPENDENCY_TOML));
}

Expand Down Expand Up @@ -162,11 +164,24 @@ public void setDependencyConfigList(List<DependencyConfig> dependencyConfigList)
this.dependencyConfigList = dependencyConfigList;
}

public String getParentPackage() {
return parentPackage;
public Map<String, String> getProfileDependencies() {
return profileDependencies;
}

public void setParentPackage(String parentPackage) {
this.parentPackage = parentPackage;
public void setProfileDependencies(List<String> customDependencies) {
for (String dependency : customDependencies) {
// This regex will validate the profile dependency pattern <profile_url>=<Ballerina_org_name>/<Ballerina_package_name>
String regex = "^https?://(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}(?:/[^\\s]*)?=[a-zA-Z0-9-]+/[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)+$";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(dependency);
if (matcher.matches()) {
String[] dependentPackageAndProfileUrl = dependency.split("=");
this.profileDependencies.put(dependentPackageAndProfileUrl[0], dependentPackageAndProfileUrl[1]);
} else {
throw new RuntimeException("Provided profile dependency is wrong: " + dependency + " " +
"It should be in the <profile_url>=<Ballerina_org>/<Ballerina_package_name> pattern, " +
"e-g: http://hl7.org/fhir/uv/=ballerinax/health.fhir.uv");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.AnnotationElement;
import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.BallerinaDataType;
import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.DataTypeDefinitionAnnotation;
import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.DataTypeProfile;
import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.DatatypeTemplateContext;
import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.Element;
import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.ExtendedElement;
Expand Down Expand Up @@ -67,8 +68,8 @@ public class ResourceContextGenerator {
private ResourceTemplateContext resourceTemplateContextInstance;
private final Map<String, ResourceTemplateContext> resourceTemplateContextMap;
private final Map<String, String> resourceNameTypeMap;

private final Map<String, DatatypeTemplateContext> datatypeTemplateContextMap;
private final Set<String> profileDependencies = new HashSet<>();

public ResourceContextGenerator(BallerinaPackageGenToolConfig config, FHIRImplementationGuide ig,
Map<String, DatatypeTemplateContext> datatypeTemplateContextMap) {
Expand Down Expand Up @@ -115,8 +116,20 @@ private void populateResourceTemplateContexts(FHIRImplementationGuide ig) {
markExtendedElements(snapshotElement);
populateResourceSliceElementsMap(snapshotElement);
populateResourceElementMap(snapshotElement);

Map<String, DataTypeProfile> profiles = snapshotElement.getProfiles();
profiles.keySet().stream()
.flatMap(key -> toolConfig.getPackageConfig().getProfileDependencies().keySet().stream()
.filter(key::startsWith)
.map(profile -> toolConfig.getPackageConfig().getProfileDependencies().get(profile)))
.distinct()
.forEach(profileDependencies::add);
}

Set<String> resourceDependencies = this.resourceTemplateContextInstance.getResourceDependencies();
resourceDependencies.addAll(profileDependencies);
this.resourceTemplateContextInstance.setResourceDependencies(resourceDependencies);

for (Element resourceElement : this.resourceTemplateContextInstance.getResourceElements().values()) {
populateResourceExtendedElementsMap(resourceElement);
populateResourceElementAnnotationsMap(resourceElement);
Expand Down Expand Up @@ -305,9 +318,10 @@ private Element populateElement(String rootName, String name, ElementDefinition.
element.addProfile(profile.getValue(), profileType);
}
//check for prefix when non R4 profiles are available
if (!profile.getValue().startsWith(ToolConstants.FHIR_R4_DEFINITION_URL)) {
if (!StringUtils.isEmpty(toolConfig.getPackageConfig().getParentPackage())) {
String prefix = CommonUtil.getSplitTokenAt(toolConfig.getPackageConfig().getParentPackage(), "\\.", ToolConstants.TokenPosition.END);
for (String profileUrl : toolConfig.getPackageConfig().getProfileDependencies().keySet()) {
if (profile.getValue().startsWith(profileUrl)) {
String s = toolConfig.getPackageConfig().getProfileDependencies().get(profileUrl);
String prefix = CommonUtil.getSplitTokenAt(s, "\\.", ToolConstants.TokenPosition.END);
element.getProfiles().get(profile.getValue()).setPrefix(prefix);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,17 +136,12 @@ private TemplateContext createTemplateContextForResourceSkeletons(ResourceTempla
templateContext.setProperty("importIdentifier", this.resourceProperties.get("importIdentifier"));

Set<String> resourceDependencies = new TreeSet<>();
if (!(boolean)this.resourceProperties.get("isBasePackage"))
if (!(boolean)this.resourceProperties.get("isBasePackage")){
resourceDependencies.add((String) this.resourceProperties.get("basePackage"));
}

Optional<String> dependency = resourceTemplateContext.getResourceDependencies()
.stream()
.filter(d -> d.equals(CONSTRAINTS_LIB_IMPORT))
.findFirst();

dependency.ifPresent(resourceDependencies::add);
resourceDependencies.addAll(resourceTemplateContext.getResourceDependencies());
templateContext.setProperty("imports", resourceDependencies);

return templateContext;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,6 @@ public class FhirSubCmd implements BLauncherCmd {
@CommandLine.Option(names = {"--package-version"}, description = "version of the Ballerina package")
private String packageVersion;

@CommandLine.Option(names = {"--parent-package"}, description = "fully qualified name of the parent " +
"package. (if exists)")
private String parentPackage;

@CommandLine.Option(names = "--included-profile", description = "Profiles to be included in the template")
private String[] includedProfiles;

Expand All @@ -92,6 +88,9 @@ public class FhirSubCmd implements BLauncherCmd {
@CommandLine.Option(names = "--dependent-package", description = "Dependent package name for the templates to be generated")
private String dependentPackage;

@CommandLine.Option(names = "--profile-dependencies", description = "Dependent package name for the templates to be generated")
private String[] profileDependencies;


@CommandLine.Parameters(description = "Custom arguments")
private List<String> argList;
Expand Down Expand Up @@ -213,7 +212,7 @@ public boolean engageSubCommand(List<String> argList) {
argsMap.put("--included-profile", includedProfiles);
argsMap.put("--excluded-profile", excludedProfiles);
argsMap.put("--dependent-package", dependentPackage);
argsMap.put("--parent-package", parentPackage);
argsMap.put("--profile-dependencies", profileDependencies);
getTargetOutputPath();
//spec path is the last argument
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package io.ballerina.health.cmd.handler;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.ballerina.health.cmd.core.config.HealthCmdConfig;
Expand All @@ -45,7 +46,7 @@ public class FhirPackageGenHandler implements Handler {
private String packageName;
private String orgName;
private String version;
private String parentPackage;
private String[] profileDependencies;

private JsonObject configJson;
private PrintStream printStream;
Expand All @@ -71,15 +72,21 @@ public void setArgs(Map<String, Object> argsMap) {
this.packageName = (String) argsMap.get("--package-name");
this.orgName = (String) argsMap.get("--org-name");
this.version = (String) argsMap.get("--package-version");
this.parentPackage = (String) argsMap.get("--parent-package");

this.profileDependencies = (String[]) argsMap.get("--profile-dependencies");
}

/**
* @param specificationPath The directory which contains the profile definitions like resources, datatypes, code systems etc
* @param targetOutputPath The directory which will contain the generated Ballerina package once the tool got executed successfully
* @return true if tool got executed successfully
*/
@Override
public boolean execute(String specificationPath, String targetOutputPath) {

// Holds the default configs
JsonElement toolExecConfigs = null;
if (configJson != null) {
// Read default values from resource/tool-config.json -> fhir.tools.package
toolExecConfigs = configJson.getAsJsonObject("fhir").getAsJsonObject("tools").getAsJsonObject(HealthCmdConstants.CMD_MODE_PACKAGE);
} else {
printStream.println(ErrorMessages.CONFIG_PARSE_ERROR);
Expand All @@ -90,20 +97,19 @@ public boolean execute(String specificationPath, String targetOutputPath) {
JsonObject toolExecConfig = toolExecConfigs.getAsJsonObject();

//override tool level configs here

Tool tool;
TemplateGenerator mainTemplateGenerator = null;
try {
ClassLoader classLoader = this.getClass().getClassLoader();
String configClassName = "org.wso2.healthcare.fhir.ballerina.packagegen.tool.config." +
"BallerinaPackageGenToolConfig";
Class<?> configClazz = classLoader.loadClass(configClassName);
String toolClassName = "org.wso2.healthcare.fhir.ballerina.packagegen.tool.BallerinaPackageGenTool";
Class<?> toolClazz = classLoader.loadClass(toolClassName);

ToolConfig toolConfigInstance = (ToolConfig) configClazz.getConstructor().newInstance();
toolConfigInstance.setTargetDir(targetOutputPath);
toolConfigInstance.setToolName(HealthCmdConstants.CMD_MODE_PACKAGE);

// Feed default project configs to ToolConfig instance
toolConfigInstance.configure(new JsonConfigType(toolExecConfig.getAsJsonObject().getAsJsonObject("config")));

//override default configs for package-gen mode with user provided configs
Expand All @@ -119,14 +125,20 @@ public boolean execute(String specificationPath, String targetOutputPath) {
JsonElement overrideConfig = new Gson().toJsonTree(version.toLowerCase());
toolConfigInstance.overrideConfig("packageConfig.version", overrideConfig);
}
if (parentPackage == null || parentPackage.isEmpty()) {
parentPackage = "";
if (profileDependencies != null && profileDependencies.length > 0) {
JsonArray dependenciesArray = new JsonArray();
for (String dependency : profileDependencies) {
dependenciesArray.add(dependency);
}
JsonElement overrideConfig = new Gson().toJsonTree(dependenciesArray);
toolConfigInstance.overrideConfig("packageConfig.profile.dependencies", overrideConfig);
}
JsonElement overrideConfig = new Gson().toJsonTree(parentPackage);
toolConfigInstance.overrideConfig("packageConfig.parentPackage", overrideConfig);

String toolClassName = "org.wso2.healthcare.fhir.ballerina.packagegen.tool.BallerinaPackageGenTool";
Class<?> toolClazz = classLoader.loadClass(toolClassName);
tool = (Tool) toolClazz.getConstructor().newInstance();
tool.initialize(toolConfigInstance);

fhirToolLib.getToolImplementations().putIfAbsent(HealthCmdConstants.CMD_MODE_PACKAGE, tool);
mainTemplateGenerator = tool.execute(fhirToolLib.getToolContext());
} catch (ClassNotFoundException e) {
Expand Down
21 changes: 15 additions & 6 deletions native/health-cli/src/main/resources/ballerina-health.help
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,21 @@ COMMANDS
to be generated. This is a MANDATORY input in ‘package’ mode.
Refer https://ballerina.io/learn/package-references/#the-name-field

--parent-package <name-of-parent-package>
Only applicable in ‘package’ mode. Name of the "parent" Ballerina
package. If some Ballerina package other than "ballerinax/health.fhir.r4"
is to be used as the parent package, specify the fully qualified name
of that package. This is an OPTIONAL input.
Refer https://ballerina.io/learn/package-references/#the-name-field
--profile-dependencies <profile-url>=<dependent-package-name>
This option is only applicable in ‘package’ mode and can accept multiple values.
Each value must be provided as a key-value pair, where:
1. <profile-url> is the base URL of the dependent package.
2. <dependent-package-name> is the fully qualified name of the corresponding Ballerina package.
This is an optional input.

For example, if your profile depends on the US Core profile, you should specify it like this:
<base-url-of-dependent-package>=<full-qualified-name-of-ballerina-package>
http://hl7.org/fhir/us/core/=ballerinax/health.fhir.r4.uscore501

Note: The dependent package (e.g., ballerinax/health.fhir.r4.uscore501) must already be published.

Ensure you follow the naming conventions for Ballerina packages. For more details, refer to the
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
Expand Down

0 comments on commit d5519e0

Please sign in to comment.