From ffac34e1d79d5650674702b02146f7d45cec015b Mon Sep 17 00:00:00 2001 From: Kevin Knight Date: Fri, 15 Dec 2023 16:34:29 -0500 Subject: [PATCH] Add service registration command --- .../AbstractLSPGrammarExtension.java | 47 ++++++--- .../extension/PureLSPGrammarExtension.java | 31 ++++-- .../extension/ServiceLSPGrammarExtension.java | 95 ++++++++++++++++++- 3 files changed, 152 insertions(+), 21 deletions(-) diff --git a/legend-engine-ide-lsp-default-extensions/src/main/java/org/finos/legend/engine/ide/lsp/extension/AbstractLSPGrammarExtension.java b/legend-engine-ide-lsp-default-extensions/src/main/java/org/finos/legend/engine/ide/lsp/extension/AbstractLSPGrammarExtension.java index 03343bf4..c2bf942a 100644 --- a/legend-engine-ide-lsp-default-extensions/src/main/java/org/finos/legend/engine/ide/lsp/extension/AbstractLSPGrammarExtension.java +++ b/legend-engine-ide-lsp-default-extensions/src/main/java/org/finos/legend/engine/ide/lsp/extension/AbstractLSPGrammarExtension.java @@ -17,19 +17,11 @@ import com.fasterxml.jackson.core.StreamReadFeature; import com.fasterxml.jackson.core.StreamWriteFeature; import com.fasterxml.jackson.databind.json.JsonMapper; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.UncheckedIOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.ServiceLoader; -import java.util.function.Consumer; import org.eclipse.collections.api.factory.Lists; import org.eclipse.collections.api.factory.Maps; import org.eclipse.collections.api.list.ImmutableList; import org.eclipse.collections.api.list.MutableList; +import org.eclipse.collections.impl.block.function.checked.ThrowingFunction; import org.eclipse.collections.impl.utility.Iterate; import org.eclipse.collections.impl.utility.ListIterate; import org.finos.legend.engine.ide.lsp.extension.declaration.LegendDeclaration; @@ -76,6 +68,19 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.function.Consumer; + abstract class AbstractLSPGrammarExtension implements LegendLSPGrammarExtension { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractLSPGrammarExtension.class); @@ -478,6 +483,11 @@ protected CompileResult tryCompile(GlobalState globalState, DocumentState docume } protected PureModelContextData buildPureModelContextData(GlobalState globalState) + { + return pureModelContextDataBuilder(globalState).build(); + } + + protected PureModelContextData.Builder pureModelContextDataBuilder(GlobalState globalState) { PureModelContextData.Builder builder = PureModelContextData.newBuilder(); globalState.forEachDocumentState(docState -> docState.forEachSectionState(secState -> @@ -492,7 +502,7 @@ protected PureModelContextData buildPureModelContextData(GlobalState globalState builder.addElements(parseResult.getElements()); } })); - return builder.build(); + return builder; } protected PureModelContextData deserializePMCD(String json) @@ -527,7 +537,22 @@ protected boolean isEngineServerConfigured() return this.engineServerClient.isServerConfigured(); } + protected String postEngineServer(String path, Object payload) + { + return postEngineServer(path, payload, stream -> + { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + stream.transferTo(bytes); + return bytes.toString(StandardCharsets.UTF_8); + }); + } + protected T postEngineServer(String path, Object payload, Class responseType) + { + return postEngineServer(path, payload, stream -> getProtocolMapper().readValue(stream, responseType)); + } + + protected T postEngineServer(String path, Object payload, ThrowingFunction consumer) { if (!isEngineServerConfigured()) { @@ -538,7 +563,7 @@ protected T postEngineServer(String path, Object payload, Class responseT { JsonMapper mapper = getProtocolMapper(); String payloadJson = mapper.writeValueAsString(payload); - return this.engineServerClient.post(path, payloadJson, stream -> mapper.readValue(stream, responseType)); + return this.engineServerClient.post(path, payloadJson, consumer); } catch (IOException e) { diff --git a/legend-engine-ide-lsp-default-extensions/src/main/java/org/finos/legend/engine/ide/lsp/extension/PureLSPGrammarExtension.java b/legend-engine-ide-lsp-default-extensions/src/main/java/org/finos/legend/engine/ide/lsp/extension/PureLSPGrammarExtension.java index f842436d..363d1ffc 100644 --- a/legend-engine-ide-lsp-default-extensions/src/main/java/org/finos/legend/engine/ide/lsp/extension/PureLSPGrammarExtension.java +++ b/legend-engine-ide-lsp-default-extensions/src/main/java/org/finos/legend/engine/ide/lsp/extension/PureLSPGrammarExtension.java @@ -15,6 +15,8 @@ package org.finos.legend.engine.ide.lsp.extension; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.StreamReadFeature; +import com.fasterxml.jackson.core.StreamWriteFeature; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.json.JsonMapper; import org.eclipse.collections.api.factory.Lists; @@ -87,10 +89,7 @@ public class PureLSPGrammarExtension extends AbstractLegacyParserLSPGrammarExten private static final String EXEC_FUNCTION_ID = "legend.pure.executeFunction"; private static final String EXEC_FUNCTION_TITLE = "Execute function"; - private static final JsonMapper JSON = PureProtocolObjectMapperFactory.withPureProtocolExtensions(JsonMapper.builder() - .enable(SerializationFeature.INDENT_OUTPUT) - .serializationInclusion(JsonInclude.Include.NON_NULL) - .build()); + private JsonMapper functionResultMapper; public PureLSPGrammarExtension() { @@ -121,8 +120,8 @@ protected void collectCommands(SectionState sectionState, PackageableElement ele public Iterable execute(SectionState section, String entityPath, String commandId, Map executableArgs) { return EXEC_FUNCTION_ID.equals(commandId) ? - executeFunction(section, entityPath) : - super.execute(section, entityPath, commandId, executableArgs); + executeFunction(section, entityPath) : + super.execute(section, entityPath, commandId, executableArgs); } @Override @@ -296,7 +295,7 @@ private String getConstantValueResult(Object value) } try { - return JSON.writeValueAsString(value); + return getFunctionResultMapper().writeValueAsString(value); } catch (Exception e) { @@ -304,4 +303,22 @@ private String getConstantValueResult(Object value) } return value.toString(); } + + private JsonMapper getFunctionResultMapper() + { + synchronized (this) + { + if (this.functionResultMapper == null) + { + this.functionResultMapper = PureProtocolObjectMapperFactory.withPureProtocolExtensions(JsonMapper.builder() + .disable(StreamWriteFeature.AUTO_CLOSE_TARGET) + .disable(StreamReadFeature.AUTO_CLOSE_SOURCE) + .enable(SerializationFeature.INDENT_OUTPUT) + .enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS) + .serializationInclusion(JsonInclude.Include.NON_NULL) + .build()); + } + return this.functionResultMapper; + } + } } diff --git a/legend-engine-ide-lsp-default-extensions/src/main/java/org/finos/legend/engine/ide/lsp/extension/ServiceLSPGrammarExtension.java b/legend-engine-ide-lsp-default-extensions/src/main/java/org/finos/legend/engine/ide/lsp/extension/ServiceLSPGrammarExtension.java index 00009bc0..a40e52a3 100644 --- a/legend-engine-ide-lsp-default-extensions/src/main/java/org/finos/legend/engine/ide/lsp/extension/ServiceLSPGrammarExtension.java +++ b/legend-engine-ide-lsp-default-extensions/src/main/java/org/finos/legend/engine/ide/lsp/extension/ServiceLSPGrammarExtension.java @@ -14,6 +14,12 @@ package org.finos.legend.engine.ide.lsp.extension; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.StreamReadFeature; +import com.fasterxml.jackson.core.StreamWriteFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; import org.eclipse.collections.api.factory.Lists; import org.eclipse.collections.api.list.MutableList; import org.eclipse.collections.impl.utility.Iterate; @@ -25,6 +31,14 @@ import org.finos.legend.engine.plan.execution.PlanExecutor; import org.finos.legend.engine.plan.generation.extension.PlanGeneratorExtension; import org.finos.legend.engine.plan.generation.transformers.PlanTransformer; +import org.finos.legend.engine.protocol.Protocol; +import org.finos.legend.engine.protocol.pure.PureClientVersions; +import org.finos.legend.engine.protocol.pure.v1.PureProtocolObjectMapperFactory; +import org.finos.legend.engine.protocol.pure.v1.model.context.AlloySDLC; +import org.finos.legend.engine.protocol.pure.v1.model.context.PackageableElementPointer; +import org.finos.legend.engine.protocol.pure.v1.model.context.PackageableElementType; +import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextData; +import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextPointer; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.PackageableElement; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.service.Service; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.service.ServiceTestSuite; @@ -52,6 +66,11 @@ public class ServiceLSPGrammarExtension extends AbstractSectionParserLSPGrammarE private static final String RUN_LEGACY_TESTS_COMMAND_ID = "legend.service.runLegacyTests"; private static final String RUN_LEGACY_TESTS_COMMAND_TITLE = "Run legacy tests"; + private static final String REGISTER_SERVICE_COMMAND_ID = "legend.service.registerService"; + private static final String REGISTER_SERVICE_COMMAND_TITLE = "Register service"; + + private JsonMapper resultMapper; + public ServiceLSPGrammarExtension() { super(ServiceParserExtension.NAME, new ServiceParserExtension()); @@ -81,6 +100,10 @@ protected void collectCommands(SectionState sectionState, PackageableElement ele if (element instanceof Service) { Service service = (Service) element; + if (isEngineServerConfigured()) + { + consumer.accept(REGISTER_SERVICE_COMMAND_ID, REGISTER_SERVICE_COMMAND_TITLE, service.sourceInformation); + } if (service.test != null) { consumer.accept(RUN_LEGACY_TESTS_COMMAND_ID, RUN_LEGACY_TESTS_COMMAND_TITLE, service.sourceInformation); @@ -91,9 +114,21 @@ protected void collectCommands(SectionState sectionState, PackageableElement ele @Override public Iterable execute(SectionState section, String entityPath, String commandId, Map executableArgs) { - return RUN_LEGACY_TESTS_COMMAND_ID.equals(commandId) ? - runLegacyServiceTest(section, entityPath) : - super.execute(section, entityPath, commandId, executableArgs); + switch (commandId) + { + case RUN_LEGACY_TESTS_COMMAND_ID: + { + return runLegacyServiceTest(section, entityPath); + } + case REGISTER_SERVICE_COMMAND_ID: + { + return registerService(section, entityPath); + } + default: + { + return super.execute(section, entityPath, commandId, executableArgs); + } + } } private Iterable runLegacyServiceTest(SectionState section, String entityPath) @@ -177,4 +212,58 @@ private Type toResultType(TestResult testResult) } } } + + private Iterable registerService(SectionState section, String entityPath) + { + try + { + Protocol serializer = new Protocol("pure", PureClientVersions.production); + PureModelContextPointer origin = new PureModelContextPointer(); + origin.serializer = serializer; + origin.sdlcInfo = new AlloySDLC(); + origin.sdlcInfo.baseVersion = "latest"; + origin.sdlcInfo.version = "none"; + origin.sdlcInfo.packageableElementPointers = Collections.singletonList(new PackageableElementPointer(PackageableElementType.SERVICE, entityPath)); + PureModelContextData pmcd = pureModelContextDataBuilder(section.getDocumentState().getGlobalState()) + .withSerializer(serializer) + .withOrigin(origin) + .build(); + String response = postEngineServer("/service/v1/register_fullInteractive", pmcd); + try + { + // Try to parse it as JSON and then format it prettily + JsonMapper mapper = getResultMapper(); + JsonNode node = mapper.readTree(response); + String formatted = mapper.writeValueAsString(node); + return Collections.singletonList(LegendExecutionResult.newResult(entityPath, Type.SUCCESS, formatted)); + } + catch (Exception ignore) + { + // Couldn't format as JSON + return Collections.singletonList(LegendExecutionResult.newResult(entityPath, Type.SUCCESS, response)); + } + } + catch (Exception e) + { + return Collections.singletonList(errorResult(e, entityPath)); + } + } + + private JsonMapper getResultMapper() + { + synchronized (this) + { + if (this.resultMapper == null) + { + this.resultMapper = PureProtocolObjectMapperFactory.withPureProtocolExtensions(JsonMapper.builder() + .disable(StreamWriteFeature.AUTO_CLOSE_TARGET) + .disable(StreamReadFeature.AUTO_CLOSE_SOURCE) + .enable(SerializationFeature.INDENT_OUTPUT) + .enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS) + .serializationInclusion(JsonInclude.Include.NON_NULL) + .build()); + } + return this.resultMapper; + } + } }