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

(Re-)add support for remote repositories #77

Merged
merged 9 commits into from
Aug 15, 2024
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 @@ -2,10 +2,10 @@

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import org.apache.commons.lang3.tuple.Pair;
import org.cqframework.cql.cql2elm.CqlTranslatorOptions;
Expand Down Expand Up @@ -35,6 +35,7 @@
import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings.VALUESET_MEMBERSHIP_MODE;
import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings.VALUESET_PRE_EXPANSION_MODE;
import org.opencds.cqf.fhir.utility.repository.ProxyRepository;
import org.opencds.cqf.fhir.utility.repository.RestRepository;
import org.opencds.cqf.fhir.utility.repository.ig.IgRepository;
import org.slf4j.LoggerFactory;
import picocli.CommandLine.ArgGroup;
Expand Down Expand Up @@ -95,7 +96,7 @@
public List<ParameterParameter> parameters;

@Option(names = {"-e", "--expression"})
public String[] expression;
public Set<String> expression;

@ArgGroup(multiplicity = "0..1", exclusive = false)
public ContextParameter context;
Expand Down Expand Up @@ -125,10 +126,9 @@
}
}

private static class Logger implements ILoggingService {

private final org.slf4j.Logger log = LoggerFactory.getLogger(Logger.class);
private static final org.slf4j.Logger log = LoggerFactory.getLogger(Logger.class);

private static class Logger implements ILoggingService {
@Override
public void logMessage(String s) {
log.warn(s);
Expand Down Expand Up @@ -198,15 +198,9 @@
for (LibraryParameter library : libraries) {
var libraryPath = Paths.get(Uris.parseOrNull(library.libraryUrl));

var modelPath = library.model != null
? Paths.get(Uris.parseOrNull(library.model.modelUrl))
: null;
var modelUrl = library.model != null ? library.model.modelUrl : null;

var terminologyPath = library.terminologyUrl != null
? Paths.get(Uris.parseOrNull(library.terminologyUrl))
: null;

var repository = createRepository(fhirContext, terminologyPath, modelPath);
var repository = createRepository(fhirContext, library.terminologyUrl, modelUrl);
var engine = Engines.forRepositoryAndSettings(
evaluationSettings, repository, null, new NpmProcessor(igContext), true);

Expand All @@ -226,30 +220,34 @@
contextParameter = Pair.of(library.context.contextName, library.context.contextValue);
}

EvaluationResult result = engine.evaluate(identifier, contextParameter);
EvaluationResult result = engine.evaluate(identifier, library.expression, contextParameter);

writeResult(result);
}

return 0;
}

private Repository createRepository(FhirContext fhirContext, Path terminologyPath, Path modelPath) {
private Repository createRepository(FhirContext fhirContext, String terminologyUrl, String modelUrl) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though behavior around the terminology path is not affected by this change, I've made it a String as well for consistency. createRepository is now responsible for turning the given URLs, if present, into the appropriate form for a repository.

Repository data = null;
Repository terminology = null;

if (terminologyPath == null && modelPath == null) {
if (terminologyUrl == null && modelUrl == null) {
return new NoOpRepository(fhirContext);
}

if (modelPath != null) {
data = new IgRepository(fhirContext, modelPath);
if (modelUrl == null) {
data = new NoOpRepository(fhirContext);
} else if (modelUrl.startsWith("file:///")) {
data = new IgRepository(fhirContext, Paths.get(Uris.parseOrNull(modelUrl)));
} else if (modelUrl.startsWith("http://") || modelUrl.startsWith(("https://"))) {
data = new RestRepository(fhirContext.newRestfulGenericClient(modelUrl));

Check warning on line 244 in ls/server/src/main/java/org/opencds/cqf/cql/ls/server/command/CqlCommand.java

View check run for this annotation

Codecov / codecov/patch

ls/server/src/main/java/org/opencds/cqf/cql/ls/server/command/CqlCommand.java#L244

Added line #L244 was not covered by tests
} else {
data = new NoOpRepository(fhirContext);
}

if (terminologyPath != null) {
terminology = new IgRepository(fhirContext, terminologyPath);
if (terminologyUrl != null) {
terminology = new IgRepository(fhirContext, Paths.get(Uris.parseOrNull(terminologyUrl)));
} else {
terminology = new NoOpRepository(fhirContext);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package org.opencds.cqf.cql.ls.server.command;

import static org.junit.jupiter.api.Assertions.*;

import com.google.gson.JsonParser;
import java.io.File;
import java.net.URI;
import java.util.Arrays;
import java.util.stream.Collectors;
import org.apache.commons.lang3.SystemUtils;
import org.eclipse.lsp4j.ExecuteCommandParams;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.opencds.cqf.cql.ls.core.ContentService;
import org.opencds.cqf.cql.ls.server.manager.IgContextManager;
import org.opencds.cqf.cql.ls.server.service.TestContentService;

class DebugCqlCommandContributionTest {

private static final String FILE_UNC_PREFIX = "file:///";

private static DebugCqlCommandContribution contribution;

@BeforeAll
static void beforeAll() {
ContentService cs = new TestContentService();
contribution = new DebugCqlCommandContribution(new IgContextManager((cs)));
}

@Test
void getCommands() {
assertEquals(1, contribution.getCommands().size());
assertEquals(
"org.opencds.cqf.cql.ls.plugin.debug.startDebugSession",
contribution.getCommands().toArray()[0]);
}

@Test
void withOnlyLibrary() {
ExecuteCommandParams params = new ExecuteCommandParams();

String libraryPath = normalizePath(
"file://" + System.getProperty("user.dir") + "/src/test/resources/org/opencds/cqf/cql/ls/server/");

params.setCommand("org.opencds.cqf.cql.ls.plugin.debug.startDebugSession");
params.setArguments(Arrays.asList("cql", "-fv=R4", "-m=FHIR", "-ln=One", "-lu=" + libraryPath).stream()
.map(str -> "\"" + str + "\"")
.map(JsonParser::parseString)
.collect(Collectors.toList()));
Object result = contribution.executeCommand(params).join();
assertInstanceOf(String.class, result);
assertTrue(((String) result).trim().endsWith("One=1"));
}

@Test
void withTerminology() {
ExecuteCommandParams params = new ExecuteCommandParams();

String libraryPath = normalizePath(
"file://" + System.getProperty("user.dir") + "/src/test/resources/org/opencds/cqf/cql/ls/server/");

params.setCommand("org.opencds.cqf.cql.ls.plugin.debug.startDebugSession");
params.setArguments(
Arrays.asList("cql", "-fv=R4", "-m=FHIR", "-ln=One", "-lu=" + libraryPath, "-t=" + libraryPath).stream()
.map(str -> "\"" + str + "\"")
.map(JsonParser::parseString)
.collect(Collectors.toList()));
Object result = contribution.executeCommand(params).join();
assertInstanceOf(String.class, result);
assertTrue(((String) result).trim().endsWith("One=1"));
}

@Test
void withFileModel() {
ExecuteCommandParams params = new ExecuteCommandParams();

String modelPath = normalizePath(
"file://" + System.getProperty("user.dir") + "/src/test/resources/org/opencds/cqf/cql/ls/server/");
String libraryPath = normalizePath(
"file://" + System.getProperty("user.dir") + "/src/test/resources/org/opencds/cqf/cql/ls/server/");

params.setCommand("org.opencds.cqf.cql.ls.plugin.debug.startDebugSession");
params.setArguments(Arrays.asList(
"cql",
"-fv=R4",
"-m=FHIR",
"-mu=" + modelPath,
"-ln=One",
"-lu=" + libraryPath,
"-t=" + libraryPath)
.stream()
.map(str -> "\"" + str + "\"")
.map(JsonParser::parseString)
.collect(Collectors.toList()));
Object result = contribution.executeCommand(params).join();
assertInstanceOf(String.class, result);
assertTrue(((String) result).trim().endsWith("One=1"));
}

// @Test
// void withRemoteModel() {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requires us to stand up a dummy HTTP server which is a rabbit hole I'm not gonna go down now, but this test does work locally with a FHIR server running, so it still has some value.

// ExecuteCommandParams params = new ExecuteCommandParams();
//
// String modelPath = "http://localhost:8000";
// String libraryPath = normalizePath("file://" + System.getProperty("user.dir") +
// "/src/test/resources/org/opencds/cqf/cql/ls/server/");
//
// params.setCommand("org.opencds.cqf.cql.ls.plugin.debug.startDebugSession");
// params.setArguments(Arrays.asList(
// "cql",
// "-fv=R4",
// "-m=FHIR",
// "-mu=" + modelPath,
// "-ln=One",
// "-lu=" + libraryPath,
// "-t=" + libraryPath
// ).stream().map(str -> "\"" + str + "\"").map(JsonParser::parseString).collect(Collectors.toList()));
// Object result = contribution.executeCommand(params).join();
// assertInstanceOf(String.class, result);
// assertTrue(((String) result).trim().endsWith("One=1"));
// }

@Test
void withRootDir() {
ExecuteCommandParams params = new ExecuteCommandParams();

String libraryPath = normalizePath(
"file://" + System.getProperty("user.dir") + "/src/test/resources/org/opencds/cqf/cql/ls/server/");

params.setCommand("org.opencds.cqf.cql.ls.plugin.debug.startDebugSession");
params.setArguments(
Arrays.asList("cql", "-fv=R4", "-m=FHIR", "--root-dir=" + libraryPath, "-ln=One", "-lu=" + libraryPath)
.stream()
.map(str -> "\"" + str + "\"")
.map(JsonParser::parseString)
.collect(Collectors.toList()));
Object result = contribution.executeCommand(params).join();
assertInstanceOf(String.class, result);
assertTrue(((String) result).trim().endsWith("One=1"));
}

private static String normalizePath(String path) {
if (SystemUtils.IS_OS_WINDOWS && path.startsWith(FILE_UNC_PREFIX)) {
String wPath = path;
try {
URI uri = new URI(path);
wPath = new File(uri.getSchemeSpecificPart()).toURI().getRawPath();
} catch (Exception e) {
}

return wPath.replace('\\', '/');
}

return path.replace('\\', '/');
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<library xmlns="urn:hl7-org:elm:r1" xmlns:t="urn:hl7-org:elm-types:r1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:fhir="http://hl7.org/fhir" xmlns:qdm43="urn:healthit-gov:qdm:v4_3" xmlns:qdm53="urn:healthit-gov:qdm:v5_3" xmlns:a="urn:hl7-org:cql-annotations:r1">
<Library xmlns="urn:hl7-org:elm:r1" xmlns:t="urn:hl7-org:elm-types:r1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:fhir="http://hl7.org/fhir" xmlns:qdm43="urn:healthit-gov:qdm:v4_3" xmlns:qdm53="urn:healthit-gov:qdm:v5_3" xmlns:a="urn:hl7-org:cql-annotations:r1">
<annotation translatorVersion="3.3.2" translatorOptions="EnableAnnotations,EnableLocators,DisableListDemotion,DisableListPromotion" signatureLevel="None" xsi:type="a:CqlToElmInfo"/>
<annotation xsi:type="a:Annotation">
<a:s r="2">
<a:s>library One</a:s>
</a:s>
</annotation>
<identifier id="One"/>
<id value="One" />
<schemaIdentifier id="urn:hl7-org:elm" version="r1"/>
<usings>
<def localIdentifier="System" uri="urn:hl7-org:elm-types:r1"/>
Expand All @@ -22,4 +22,4 @@
<expression localId="1" locator="4:5" valueType="t:Integer" value="1" xsi:type="Literal"/>
</def>
</statements>
</library>
</Library>