diff --git a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshDeploymentManifestSchema.java b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshDeploymentManifestSchema.java index 880c5e0e4..297cbdfb6 100644 --- a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshDeploymentManifestSchema.java +++ b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshDeploymentManifestSchema.java @@ -19,6 +19,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.springframework.ide.vscode.bosh.models.CachingModelProvider; import org.springframework.ide.vscode.bosh.models.CloudConfigModel; import org.springframework.ide.vscode.bosh.models.DynamicModelProvider; +import org.springframework.ide.vscode.bosh.models.StemcellsModel; import org.springframework.ide.vscode.commons.util.Assert; import org.springframework.ide.vscode.commons.util.CollectorUtil; import org.springframework.ide.vscode.commons.util.Renderable; @@ -84,11 +85,17 @@ public class BoshDeploymentManifestSchema implements YamlSchema { private YType t_var_name_def; private final ASTTypeCache astTypes; private DynamicModelProvider cloudConfigProvider; + private DynamicModelProvider stemcellsProvider; private List> defAndRefTypes; - public BoshDeploymentManifestSchema(ASTTypeCache astTypes, DynamicModelProvider cloudConfigProvider) { + public BoshDeploymentManifestSchema( + ASTTypeCache astTypes, + DynamicModelProvider cloudConfigProvider, + DynamicModelProvider stemcellsProvider + ) { this.astTypes = astTypes; this.cloudConfigProvider = new CachingModelProvider<>(cloudConfigProvider); + this.stemcellsProvider = new CachingModelProvider<>(stemcellsProvider); TYPE_UTIL = f.TYPE_UTIL; V2_TOPLEVEL_TYPE = createV2Schema(); @@ -228,7 +235,9 @@ public class BoshDeploymentManifestSchema implements YamlSchema { YBeanType t_variable = f.ybean("Variable"); addProp(t_variable, "name", t_var_name_def).isPrimary(true); - addProp(t_variable, "type", f.yenum("VariableType", "certificate", "password", "rsa", "ssh")).isRequired(true); + YType t_variable_type = f.yenum("VariableType", "certificate", "password", "rsa", "ssh") + .parseWith(ValueParsers.NE_STRING); //Overrid the parser -> no errors / warnings... in theory there could be other valid values. + addProp(t_variable, "type", t_variable_type).isRequired(true); addProp(t_variable, "options", t_params); addProp(v2Schema, "variables", f.yseq(t_variable)); diff --git a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshLanguageServer.java b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshLanguageServer.java index 541970a8e..bb062ed63 100644 --- a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshLanguageServer.java +++ b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshLanguageServer.java @@ -12,6 +12,7 @@ package org.springframework.ide.vscode.bosh; import org.springframework.ide.vscode.bosh.models.CloudConfigModel; import org.springframework.ide.vscode.bosh.models.DynamicModelProvider; +import org.springframework.ide.vscode.bosh.models.StemcellsModel; import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter; import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfoProvider; import org.springframework.ide.vscode.commons.languageserver.hover.VscodeHoverEngine; @@ -37,13 +38,13 @@ public class BoshLanguageServer extends SimpleLanguageServer { private final VscodeCompletionEngineAdapter completionEngine; - public BoshLanguageServer(DynamicModelProvider cloudConfigProvider) { + public BoshLanguageServer(DynamicModelProvider cloudConfigProvider, DynamicModelProvider stemcellsProvider) { super("vscode-bosh"); YamlAstCache asts = new YamlAstCache(); SimpleTextDocumentService documents = getTextDocumentService(); ASTTypeCache astTypeCache = new ASTTypeCache(); - BoshDeploymentManifestSchema schema = new BoshDeploymentManifestSchema(astTypeCache, cloudConfigProvider); + BoshDeploymentManifestSchema schema = new BoshDeploymentManifestSchema(astTypeCache, cloudConfigProvider, stemcellsProvider); YamlStructureProvider structureProvider = YamlStructureProvider.DEFAULT; YamlAssistContextProvider contextProvider = new SchemaBasedYamlAssistContextProvider(schema); diff --git a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/Main.java b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/Main.java index 7e1a1403d..ebcbe0bbc 100644 --- a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/Main.java +++ b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/Main.java @@ -17,6 +17,9 @@ import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp; public class Main { public static void main(String[] args) throws IOException, InterruptedException { - LaunguageServerApp.start(() -> new BoshLanguageServer(new BoshCommandCloudConfigProvider())); + LaunguageServerApp.start(() -> new BoshLanguageServer( + new BoshCommandCloudConfigProvider(), + (dc) -> null //TODO: real model provider here! + )); } } diff --git a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/BoshCommandBasedModelProvider.java b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/BoshCommandBasedModelProvider.java new file mode 100644 index 000000000..76cd3b31d --- /dev/null +++ b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/BoshCommandBasedModelProvider.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2017 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.bosh.models; + +import java.io.File; +import java.time.Duration; + +import org.springframework.ide.vscode.commons.util.Assert; +import org.springframework.ide.vscode.commons.util.ExternalCommand; +import org.springframework.ide.vscode.commons.util.ExternalProcess; +import org.springframework.ide.vscode.commons.util.text.LanguageId; +import org.springframework.ide.vscode.commons.util.text.TextDocument; +import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST; +import org.springframework.ide.vscode.commons.yaml.ast.YamlParser; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.representer.Representer; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Abstract base class to aid in implementing a Dynamic model provider that executes a bosh + * command (with `--json`) swtich and then extracts information from its json output. + */ +public abstract class BoshCommandBasedModelProvider implements DynamicModelProvider { + + private final YamlParser yamlParser; + protected final ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + protected Duration CMD_TIMEOUT = Duration.ofSeconds(10); + + protected BoshCommandBasedModelProvider() { + Representer representer = new Representer(); + representer.getPropertyUtils().setSkipMissingProperties(true); + yamlParser = new YamlParser(new Yaml()); + } + + /** + * For deserializing the output from bosh cloud-config command. + */ + public static class BoshCommandResponse { + private String[] blocks; + + @JsonProperty("Blocks") + public String[] getBlocks() { + return blocks; + } + public void setBlocks(String[] blocks) { + this.blocks = blocks; + } + } + + protected String getBlock() throws Exception { + String out = executeCommand(getCommand()); + BoshCommandResponse response = mapper.readValue(out, BoshCommandResponse.class); + String[] blocks = response.getBlocks(); + Assert.isLegal(blocks!=null); + Assert.isLegal(blocks.length==1); + return blocks[0]; + } + + protected JsonNode getJsonTree() throws Exception { + String out = executeCommand(getCommand()); + return mapper.readTree(out); + } + + protected String executeCommand(ExternalCommand command) throws Exception { + ExternalProcess process = new ExternalProcess(getWorkingDir(), command, true, CMD_TIMEOUT); + String out = process.getOut(); + return out; + } + + protected File getWorkingDir() { + return new File(".").getAbsoluteFile(); + } + + protected abstract ExternalCommand getCommand(); + + protected YamlFileAST parseYaml(String block) throws Exception { + TextDocument doc = new TextDocument(null, LanguageId.BOSH_CLOUD_CONFIG); + doc.setText(block); + YamlFileAST ast = yamlParser.getAST(doc); + return ast; + } +} diff --git a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/BoshCommandCloudConfigProvider.java b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/BoshCommandCloudConfigProvider.java index 0d2021751..095188251 100644 --- a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/BoshCommandCloudConfigProvider.java +++ b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/BoshCommandCloudConfigProvider.java @@ -10,30 +10,18 @@ *******************************************************************************/ package org.springframework.ide.vscode.bosh.models; -import java.io.File; import java.time.Duration; import java.util.Collection; import java.util.stream.Stream; -import org.springframework.ide.vscode.commons.util.Assert; import org.springframework.ide.vscode.commons.util.CollectorUtil; import org.springframework.ide.vscode.commons.util.ExternalCommand; -import org.springframework.ide.vscode.commons.util.ExternalProcess; import org.springframework.ide.vscode.commons.util.StringUtil; -import org.springframework.ide.vscode.commons.util.text.LanguageId; -import org.springframework.ide.vscode.commons.util.text.TextDocument; import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil; import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST; -import org.springframework.ide.vscode.commons.yaml.ast.YamlParser; import org.springframework.ide.vscode.commons.yaml.path.YamlPath; import org.springframework.ide.vscode.commons.yaml.path.YamlTraversal; import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext; -import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.representer.Representer; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; /** * Concrete implementation of {@link CloudConfigProvider} that runs `bosh cloud-config` command @@ -41,33 +29,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; * * @author Kris De Volder */ -public class BoshCommandCloudConfigProvider implements DynamicModelProvider { - - ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - final YamlParser yamlParser; - - private Duration CMD_TIMEOUT = Duration.ofSeconds(10); +public class BoshCommandCloudConfigProvider extends BoshCommandBasedModelProvider { public BoshCommandCloudConfigProvider() { - Representer representer = new Representer(); - representer.getPropertyUtils().setSkipMissingProperties(true); - yamlParser = new YamlParser(new Yaml()); - } - - /** - * For deserializing the output from bosh cloud-config command. - */ - public static class CloudConfigResponse { - private String[] blocks; - - @JsonProperty("Blocks") - public String[] getBlocks() { - return blocks; - } - - public void setBlocks(String[] blocks) { - this.blocks = blocks; - } } private static final YamlTraversal VM_TYPE_NAMES = YamlPath.EMPTY @@ -98,10 +62,8 @@ public class BoshCommandCloudConfigProvider implements DynamicModelProvider getVMTypes() { @@ -141,15 +103,6 @@ public class BoshCommandCloudConfigProvider implements DynamicModelProvider { + + YamlTraversal STEMCELL_NAMES = YamlPath.EMPTY + .thenValAt("Tables") + .thenAnyChild() + .thenValAt("Rows") + .thenAnyChild() + .thenValAt("name"); + + @Override + public StemcellsModel getModel(DynamicSchemaContext dc) throws Exception { + JSONCursor cursor = new JSONCursor(getJsonTree()); + return new StemcellsModel() { + + @Override + public Collection getStemcellNames() { + return getNames(STEMCELL_NAMES); + } + + private Collection getNames(YamlTraversal path) { + return path.traverseAmbiguously(cursor) + .flatMap((cursor) -> { + String text = cursor.target.asText(); + if (StringUtil.hasText(text)) { + return Stream.of(text); + } else { + return Stream.empty(); + } + }) + .collect(CollectorUtil.toImmutableSet()); + } + }; + } + + @Override + protected ExternalCommand getCommand() { + return new ExternalCommand("bosh", "stemcells", "--json"); + } + +} diff --git a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/DynamicModelProvider.java b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/DynamicModelProvider.java index 27c817576..171d0fe24 100644 --- a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/DynamicModelProvider.java +++ b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/DynamicModelProvider.java @@ -3,7 +3,8 @@ package org.springframework.ide.vscode.bosh.models; import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext; /** - * Responsible for somehow obtaining {@link CloudConfigModel} relative to a {@link DynamicSchemaContext} + * Responsible for somehow obtaining some type of `dynamic` model information relative + * to a {@link DynamicSchemaContext} */ public interface DynamicModelProvider { T getModel(DynamicSchemaContext dc) throws Exception; diff --git a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/JSONCursor.java b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/JSONCursor.java new file mode 100644 index 000000000..30ee9213b --- /dev/null +++ b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/JSONCursor.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2017 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.bosh.models; + +import java.util.stream.Stream; + +import org.springframework.ide.vscode.commons.yaml.path.YamlNavigable; +import org.springframework.ide.vscode.commons.yaml.path.YamlPath; +import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment; +import org.springframework.ide.vscode.commons.yaml.util.Streams; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * Allows using {@link YamlPath} / {@link YamlNavigable} on {@link JsonNode}s + */ +public class JSONCursor implements YamlNavigable { + + public final JsonNode target; + + public JSONCursor(JsonNode target) { + super(); + this.target = target; + } + + @Override + public Stream traverseAmbiguously(YamlPathSegment s) { + return oneStep(s).map(JSONCursor::new); + } + + private Stream oneStep(YamlPathSegment s) { + if (target==null) { + return Stream.empty(); + } + switch (s.getType()) { + case KEY_AT_KEY: { + return Streams.fromNullable(target.get(s.toPropString())); + } + case ANY_CHILD: { + return Streams.fromIterable(target); + } + case VAL_AT_INDEX: { + return Streams.fromNullable(target.get(s.toIndex())); + } + case VAL_AT_KEY: { + return Streams.fromNullable(target.get(s.toPropString())); + } + default: + throw new IllegalStateException("Missing case for "+s.getType()); + } + } + + @Override + public String toString() { + return ""+target; + } + +} diff --git a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/StemcellsModel.java b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/StemcellsModel.java new file mode 100644 index 000000000..22ed422b1 --- /dev/null +++ b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/StemcellsModel.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2017 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.bosh.models; + +import java.util.Collection; + +/** + * Represents Stemcells information as might be retrieved from bosh director. + */ +public interface StemcellsModel { + + Collection getStemcellNames(); + +} diff --git a/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshCommandCloudConfigProviderTest.java b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshCommandCloudConfigProviderTest.java index ecc3c5667..a0a551059 100644 --- a/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshCommandCloudConfigProviderTest.java +++ b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshCommandCloudConfigProviderTest.java @@ -15,6 +15,7 @@ import static org.junit.Assert.assertEquals; import org.junit.Test; import org.mockito.Mockito; import org.springframework.ide.vscode.bosh.mocks.MockCloudConfigProvider; +import org.springframework.ide.vscode.bosh.models.BoshCommandBasedModelProvider; import org.springframework.ide.vscode.bosh.models.BoshCommandCloudConfigProvider; import org.springframework.ide.vscode.bosh.models.CloudConfigModel; import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext; @@ -23,13 +24,13 @@ import com.google.common.collect.ImmutableMultiset; public class BoshCommandCloudConfigProviderTest { - public final MockCloudConfigProvider mockProvider = new MockCloudConfigProvider(); + public final BoshCommandCloudConfigProvider mockProvider = new MockCloudConfigProvider(); // For local testing only... in CI builds we don't have the means to use a real bosh director and cli. // private BoshCommandCloudConfigProvider realProvider = new BoshCommandCloudConfigProvider(); @Test public void getVMTypes() throws Exception { - BoshCommandCloudConfigProvider provider = mockProvider; + BoshCommandBasedModelProvider provider = mockProvider; DynamicSchemaContext dc = Mockito.mock(DynamicSchemaContext.class); CloudConfigModel cloudConfig = provider.getModel(dc); diff --git a/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java index 3acb9bba7..30de9531d 100644 --- a/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java +++ b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java @@ -20,7 +20,10 @@ import java.util.concurrent.TimeoutException; import org.eclipse.lsp4j.CompletionItem; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.springframework.ide.vscode.bosh.mocks.MockCloudConfigProvider; +import org.springframework.ide.vscode.bosh.models.DynamicModelProvider; +import org.springframework.ide.vscode.bosh.models.StemcellsModel; import org.springframework.ide.vscode.commons.util.text.LanguageId; import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems; import org.springframework.ide.vscode.languageserver.testharness.Editor; @@ -31,10 +34,11 @@ public class BoshEditorTest { LanguageServerHarness harness; private MockCloudConfigProvider cloudConfigProvider = new MockCloudConfigProvider(); + private DynamicModelProvider stemcellsProvider = Mockito.mock(DynamicModelProvider.class); @Before public void setup() throws Exception { harness = new LanguageServerHarness(() -> { - return new BoshLanguageServer(cloudConfigProvider) + return new BoshLanguageServer(cloudConfigProvider, stemcellsProvider) .setMaxCompletions(100); }, LanguageId.BOSH_DEPLOYMENT @@ -561,6 +565,20 @@ public class BoshEditorTest { editor.assertCompletionLabels("certificate", "password", "rsa", "ssh"); } + @Test public void variablesBlockReconciling() throws Exception { + Editor editor = harness.newEditor( + "variables:\n" + + "- name: admin-passcode\n" + + " type: something-that-might-work-in-theory\n" + //shouldn't be a warning/error + " bogus-propt: bah\n" + + " options: {}" + ); + editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY); + editor.assertProblems( + "bogus-propt|Unknown property" + ); + } + @Test public void variablesBlockHovers() throws Exception { Editor editor = harness.newEditor( "variables:\n" + diff --git a/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshLanguageServerTest.java b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshLanguageServerTest.java index c3eb682f1..c30d1195f 100644 --- a/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshLanguageServerTest.java +++ b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshLanguageServerTest.java @@ -19,7 +19,9 @@ import java.nio.file.Paths; import org.eclipse.lsp4j.InitializeResult; import org.eclipse.lsp4j.TextDocumentSyncKind; import org.junit.Test; +import org.mockito.Mockito; import org.springframework.ide.vscode.bosh.mocks.MockCloudConfigProvider; +import org.springframework.ide.vscode.bosh.models.DynamicModelProvider; import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness; public class BoshLanguageServerTest { @@ -31,7 +33,7 @@ public class BoshLanguageServerTest { @Test public void createAndInitializeServerWithWorkspace() throws Exception { LanguageServerHarness harness = new LanguageServerHarness(() -> - new BoshLanguageServer(new MockCloudConfigProvider()) + new BoshLanguageServer(new MockCloudConfigProvider(), Mockito.mock(DynamicModelProvider.class)) ); File workspaceRoot = getTestResource("/workspace/"); assertExpectedInitResult(harness.intialize(workspaceRoot)); @@ -41,7 +43,7 @@ public class BoshLanguageServerTest { public void createAndInitializeServerWithoutWorkspace() throws Exception { File workspaceRoot = null; LanguageServerHarness harness = new LanguageServerHarness(() -> - new BoshLanguageServer(new MockCloudConfigProvider()) + new BoshLanguageServer(new MockCloudConfigProvider(), Mockito.mock(DynamicModelProvider.class)) ); assertExpectedInitResult(harness.intialize(workspaceRoot)); } diff --git a/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/mocks/MockCloudConfigProvider.java b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/mocks/MockCloudConfigProvider.java index 99a28b3a0..02e96ea39 100644 --- a/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/mocks/MockCloudConfigProvider.java +++ b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/mocks/MockCloudConfigProvider.java @@ -14,6 +14,7 @@ import java.util.concurrent.Callable; import org.springframework.ide.vscode.bosh.BoshCommandCloudConfigProviderTest; import org.springframework.ide.vscode.bosh.models.BoshCommandCloudConfigProvider; +import org.springframework.ide.vscode.commons.util.ExternalCommand; import org.springframework.ide.vscode.commons.util.IOUtil; public final class MockCloudConfigProvider extends BoshCommandCloudConfigProvider { @@ -33,15 +34,15 @@ public final class MockCloudConfigProvider extends BoshCommandCloudConfigProvide * without requiring a real bosh setup. */ @Override - protected String executeBoshCloudConfigCommand() throws Exception { + protected String executeCommand(ExternalCommand cmd) throws Exception { readCount++; return cloudConfigCmdExecutor.call(); } @Override - protected String getCloudConfigBlock() throws Exception { + protected String getBlock() throws Exception { if (reader==null) { - return super.getCloudConfigBlock(); + return super.getBlock(); } readCount++; return reader.call(); diff --git a/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/models/BoshCommandStemcellsProviderTest.java b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/models/BoshCommandStemcellsProviderTest.java new file mode 100644 index 000000000..3288cd572 --- /dev/null +++ b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/models/BoshCommandStemcellsProviderTest.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2017 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.bosh.models; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.ide.vscode.bosh.BoshCommandCloudConfigProviderTest; +import org.springframework.ide.vscode.commons.util.IOUtil; +import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext; + +import com.google.common.collect.ImmutableSet; + +public class BoshCommandStemcellsProviderTest { + + private static final String MOCK_DATA_RSRC = "/cmd-out/stemcells.json";; + public BoshCommandStemcellsProvider provider = Mockito.spy(BoshCommandStemcellsProvider.class); + + @Before + public void settup() throws Exception { + Mockito.doReturn(IOUtil.toString(BoshCommandCloudConfigProviderTest.class.getResourceAsStream(MOCK_DATA_RSRC))) + .when(provider).executeCommand(Mockito.any()); + } + + @Test public void getStemcellNames() throws Exception { + assertEquals(ImmutableSet.of( + "bosh-vsphere-esxi-centos-7-go_agent", + "bosh-vsphere-esxi-ubuntu-trusty-go_agent" + ), + provider.getModel(Mockito.mock(DynamicSchemaContext.class)) + .getStemcellNames() + ); + } + +} diff --git a/headless-services/bosh-language-server/src/test/resources/cmd-out/stemcells.json b/headless-services/bosh-language-server/src/test/resources/cmd-out/stemcells.json new file mode 100644 index 000000000..be1e5d053 --- /dev/null +++ b/headless-services/bosh-language-server/src/test/resources/cmd-out/stemcells.json @@ -0,0 +1,38 @@ +{ + "Tables": [ + { + "Content": "stemcells", + "Header": { + "cid": "CID", + "cpi": "CPI", + "name": "Name", + "os": "OS", + "version": "Version" + }, + "Rows": [ + { + "cid": "sc-cd694110-4b23-4361-b5e7-b817874eadca", + "cpi": "", + "name": "bosh-vsphere-esxi-centos-7-go_agent", + "os": "centos-7", + "version": "3421.11" + }, + { + "cid": "sc-3378b7e1-5a80-4351-b521-7e9aeeb417bc", + "cpi": "", + "name": "bosh-vsphere-esxi-ubuntu-trusty-go_agent", + "os": "ubuntu-trusty", + "version": "3421.11" + } + ], + "Notes": [ + "(*) Currently deployed" + ] + } + ], + "Blocks": null, + "Lines": [ + "Using environment '10.194.4.35' as client 'admin'", + "Succeeded" + ] +} \ No newline at end of file diff --git a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/CollectorUtil.java b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/CollectorUtil.java index aee5ef083..8d9db24ed 100644 --- a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/CollectorUtil.java +++ b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/CollectorUtil.java @@ -11,6 +11,7 @@ package org.springframework.ide.vscode.commons.util; import java.util.ArrayList; +import java.util.EnumSet; import java.util.HashSet; import java.util.Set; import java.util.function.BiConsumer; @@ -62,7 +63,7 @@ public class CollectorUtil { @Override public Set characteristics() { - return ImmutableSet.of(Collector.Characteristics.UNORDERED); + return EnumSet.of(Collector.Characteristics.UNORDERED); } }; } @@ -94,7 +95,39 @@ public class CollectorUtil { @Override public Set characteristics() { - return ImmutableSet.of(Collector.Characteristics.UNORDERED); + return EnumSet.of(Collector.Characteristics.UNORDERED); + } + }; + } + + public static Collector, ImmutableSet> toImmutableSet() { + return new Collector, ImmutableSet>() { + + @Override + public Supplier> supplier() { + return ArrayList::new; + } + + @Override + public BiConsumer, T> accumulator() { + return (a, e) -> a.add(e); + } + + @Override + public BinaryOperator> combiner() { + return (a1, a2) -> { + a1.addAll(a2); + return a1; + }; + } + @Override + public Function, ImmutableSet> finisher() { + return ImmutableSet::copyOf; + } + + @Override + public Set characteristics() { + return EnumSet.of(Collector.Characteristics.UNORDERED); } }; } diff --git a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/util/Streams.java b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/util/Streams.java index f02993420..077b0cb6f 100644 --- a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/util/Streams.java +++ b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/util/Streams.java @@ -8,12 +8,12 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ - package org.springframework.ide.vscode.commons.yaml.util; import java.util.ArrayList; import java.util.stream.Collectors; import java.util.stream.Stream; +import java.util.stream.StreamSupport; public class Streams { @@ -35,4 +35,8 @@ public class Streams { return e==null ? Stream.empty() : Stream.of(e); } + public static Stream fromIterable(Iterable target) { + return StreamSupport.stream(target.spliterator(), false); + } + }