Dynamic CA for release names in releases block

This commit is contained in:
Kris De Volder
2017-07-27 10:52:54 -07:00
parent 9957e5bbf2
commit b6f2691d30
12 changed files with 346 additions and 31 deletions

View File

@@ -20,6 +20,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.ReleasesModel;
import org.springframework.ide.vscode.bosh.models.StemcellData;
import org.springframework.ide.vscode.bosh.models.StemcellModel;
import org.springframework.ide.vscode.bosh.models.StemcellsModel;
@@ -96,17 +97,20 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
private final ASTTypeCache astTypes;
private DynamicModelProvider<CloudConfigModel> cloudConfigProvider;
private DynamicModelProvider<StemcellsModel> stemcellsProvider;
private DynamicModelProvider<ReleasesModel> releasesProvider;
private List<Pair<YType, YType>> defAndRefTypes;
public BoshDeploymentManifestSchema(
YamlAstCache asts, ASTTypeCache astTypes,
DynamicModelProvider<CloudConfigModel> cloudConfigProvider,
DynamicModelProvider<StemcellsModel> stemcellsProvider
DynamicModelProvider<StemcellsModel> stemcellsProvider,
DynamicModelProvider<ReleasesModel> releasesProvider
) {
this.asts = asts;
this.astTypes = astTypes;
this.cloudConfigProvider = new CachingModelProvider<>(cloudConfigProvider);
this.stemcellsProvider = new CachingModelProvider<>(stemcellsProvider);
this.releasesProvider = new CachingModelProvider<>(releasesProvider);
TYPE_UTIL = f.TYPE_UTIL;
V2_TOPLEVEL_TYPE = createV2Schema();
@@ -147,8 +151,7 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
t_stemcell_alias_def = f.yatomic("StemcellAlias")
.parseWith(ValueParsers.NE_STRING);
t_stemcell_alias_ref = f.yenumFromDynamicValues("StemcellAlias", (dc) -> astTypes.getDefinedNames(dc, t_stemcell_alias_def));
t_release_name_def = f.yatomic("ReleaseName")
.parseWith(ValueParsers.NE_STRING);
t_release_name_def = f.yenumFromDynamicValues("ReleaseName", (dc) -> releasesProvider.getModel(dc).getReleaseNames());
t_release_name_ref = f.yenumFromDynamicValues("ReleaseName", (dc) -> astTypes.getDefinedNames(dc, t_release_name_def));
t_var_name_def = f.yatomic("VariableName")

View File

@@ -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.ReleasesModel;
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;
@@ -41,13 +42,17 @@ public class BoshLanguageServer extends SimpleLanguageServer {
private final VscodeCompletionEngineAdapter completionEngine;
public BoshLanguageServer(BoshCliConfig cliConfig, DynamicModelProvider<CloudConfigModel> cloudConfigProvider, DynamicModelProvider<StemcellsModel> stemcellsProvider) {
public BoshLanguageServer(BoshCliConfig cliConfig,
DynamicModelProvider<CloudConfigModel> cloudConfigProvider,
DynamicModelProvider<StemcellsModel> stemcellsProvider,
DynamicModelProvider<ReleasesModel> releasesProvider
) {
super("vscode-bosh");
YamlAstCache asts = new YamlAstCache();
SimpleTextDocumentService documents = getTextDocumentService();
ASTTypeCache astTypeCache = new ASTTypeCache();
BoshDeploymentManifestSchema schema = new BoshDeploymentManifestSchema(asts, astTypeCache, cloudConfigProvider, stemcellsProvider);
BoshDeploymentManifestSchema schema = new BoshDeploymentManifestSchema(asts, astTypeCache, cloudConfigProvider, stemcellsProvider, releasesProvider);
YamlStructureProvider structureProvider = YamlStructureProvider.DEFAULT;
YamlAssistContextProvider contextProvider = new SchemaBasedYamlAssistContextProvider(schema);

View File

@@ -13,6 +13,7 @@ package org.springframework.ide.vscode.bosh;
import java.io.IOException;
import org.springframework.ide.vscode.bosh.models.BoshCommandCloudConfigProvider;
import org.springframework.ide.vscode.bosh.models.BoshCommandReleasesProvider;
import org.springframework.ide.vscode.bosh.models.BoshCommandStemcellsProvider;
import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp;
@@ -22,7 +23,8 @@ public class Main {
LaunguageServerApp.start(() -> new BoshLanguageServer(
cliConfig,
new BoshCommandCloudConfigProvider(cliConfig),
new BoshCommandStemcellsProvider(cliConfig)
new BoshCommandStemcellsProvider(cliConfig),
new BoshCommandReleasesProvider(cliConfig)
));
}
}

View File

@@ -13,17 +13,22 @@ package org.springframework.ide.vscode.bosh.models;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Stream;
import org.springframework.ide.vscode.bosh.BoshCliConfig;
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.Log;
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.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.ast.YamlParser;
import org.springframework.ide.vscode.commons.yaml.path.YamlTraversal;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.representer.Representer;
@@ -124,4 +129,17 @@ public abstract class BoshCommandBasedModelProvider<T> implements DynamicModelPr
YamlFileAST ast = yamlParser.getAST(doc);
return ast;
}
protected Collection<String> getNames(JSONCursor _cursor, 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());
}
}

View File

@@ -0,0 +1,73 @@
/*******************************************************************************
* 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;
import java.util.List;
import org.springframework.ide.vscode.bosh.BoshCliConfig;
import org.springframework.ide.vscode.commons.util.CollectorUtil;
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;
public class BoshCommandReleasesProvider extends BoshCommandBasedModelProvider<ReleasesModel> {
private static final String[] COMMAND = new String[] { "stemcells", "--json" };
private static final YamlTraversal RELEASES = YamlPath.EMPTY
.thenValAt("Tables")
.thenAnyChild()
.thenValAt("Rows")
.thenAnyChild();
private static final YamlTraversal RELEASE_NAMES = RELEASES
.thenValAt("name");
public BoshCommandReleasesProvider(BoshCliConfig config) {
super(config);
}
@Override
public ReleasesModel getModel(DynamicSchemaContext dc) throws Exception {
JSONCursor cursor = new JSONCursor(getJsonTree());
return new ReleasesModel() {
@Override
public List<ReleaseData> getReleases() {
return RELEASES.traverseAmbiguously(cursor)
.map(c -> new ReleaseData(
getStringProperty(c, "name"),
getStringProperty(c, "version")
))
.collect(CollectorUtil.toImmutableList());
}
private String getStringProperty(JSONCursor c, String prop) {
c = YamlPath.EMPTY.thenValAt(prop).traverse(c);
if (c!=null) {
return c.target.asText();
}
return null;
}
@Override
public Collection<String> getReleaseNames() {
return getNames(cursor, RELEASE_NAMES);
}
};
}
@Override
protected String[] getBoshCommand() {
return COMMAND;
}
}

View File

@@ -11,12 +11,10 @@
package org.springframework.ide.vscode.bosh.models;
import java.util.Collection;
import java.util.stream.Stream;
import org.springframework.ide.vscode.bosh.BoshCliConfig;
import org.springframework.ide.vscode.commons.util.CollectorUtil;
import org.springframework.ide.vscode.commons.util.ExternalCommand;
import org.springframework.ide.vscode.commons.util.StringUtil;
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;
@@ -49,20 +47,7 @@ public class BoshCommandStemcellsProvider extends BoshCommandBasedModelProvider<
@Override
public Collection<String> getStemcellNames() {
return getNames(STEMCELL_NAMES);
}
private Collection<String> 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());
return getNames(cursor, STEMCELL_NAMES);
}
@Override
@@ -86,12 +71,12 @@ public class BoshCommandStemcellsProvider extends BoshCommandBasedModelProvider<
@Override
public Collection<String> getStemcellOss() {
return getNames(STEMCELL_OSS);
return getNames(cursor, STEMCELL_OSS);
}
@Override
public Collection<String> getVersions() {
return getNames(STEMCELL_VERSIONS);
return getNames(cursor, STEMCELL_VERSIONS);
}
};
}

View File

@@ -0,0 +1,79 @@
/*******************************************************************************
* 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;
public class ReleaseData {
private String name;
private String version;
public ReleaseData(String name, String version) {
super();
this.name = name;
this.version = version;
}
public ReleaseData() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((version == null) ? 0 : version.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ReleaseData other = (ReleaseData) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (version == null) {
if (other.version != null)
return false;
} else if (!version.equals(other.version))
return false;
return true;
}
@Override
public String toString() {
return "ReleaseData [name=" + name + ", version=" + version + "]";
}
}

View File

@@ -0,0 +1,19 @@
/*******************************************************************************
* 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;
import java.util.List;
public interface ReleasesModel {
List<ReleaseData> getReleases();
Collection<String> getReleaseNames();
}

View File

@@ -29,8 +29,11 @@ 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.BoshCommandReleasesProvider;
import org.springframework.ide.vscode.bosh.models.BoshCommandStemcellsProvider;
import org.springframework.ide.vscode.bosh.models.DynamicModelProvider;
import org.springframework.ide.vscode.bosh.models.ReleaseData;
import org.springframework.ide.vscode.bosh.models.ReleasesModel;
import org.springframework.ide.vscode.bosh.models.StemcellData;
import org.springframework.ide.vscode.bosh.models.StemcellsModel;
import org.springframework.ide.vscode.commons.util.ExternalCommand;
@@ -51,12 +54,16 @@ public class BoshEditorTest {
private BoshCliConfig cliConfig = new BoshCliConfig();
private MockCloudConfigProvider cloudConfigProvider = new MockCloudConfigProvider(cliConfig);
private DynamicModelProvider<StemcellsModel> stemcellsProvider = Mockito.mock(DynamicModelProvider.class);
private DynamicModelProvider<StemcellsModel> stemcellsProvider = mock(DynamicModelProvider.class);
private DynamicModelProvider<ReleasesModel> releasesProvider = mock(DynamicModelProvider.class);
@Before public void setup() throws Exception {
harness = new LanguageServerHarness(() -> {
return new BoshLanguageServer(cliConfig, cloudConfigProvider, (dc) -> stemcellsProvider.getModel(dc))
.setMaxCompletions(100);
return new BoshLanguageServer(cliConfig, cloudConfigProvider,
(dc) -> stemcellsProvider.getModel(dc),
(dc) -> releasesProvider.getModel(dc)
)
.setMaxCompletions(100);
},
LanguageId.BOSH_DEPLOYMENT
);
@@ -1136,6 +1143,58 @@ public class BoshEditorTest {
};
}
@Test public void contentAssistReleaseNameDef() throws Exception {
releasesProvider = provideReleasesFrom(
new ReleaseData("foo", "123.4"),
new ReleaseData("foo", "222.2"),
new ReleaseData("bar", "222.2"),
new ReleaseData("bar", "333.3")
);
Editor editor = harness.newEditor(
"releases:\n" +
"- name: <*>"
);
editor.assertContextualCompletions("<*>",
"bar<*>",
"foo<*>"
);
}
@Test public void reconcileReleaseNameDef() throws Exception {
releasesProvider = provideReleasesFrom(
new ReleaseData("foo", "123.4"),
new ReleaseData("foo", "222.2"),
new ReleaseData("bar", "222.2"),
new ReleaseData("bar", "333.3")
);
Editor editor = harness.newEditor(
"releases:\n" +
"- name: foo\n" +
"- name: bar\n" +
"- name: bogus\n"
);
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
editor.assertProblems("bogus|unknown 'ReleaseName'. Valid values are: [foo, bar]");
}
private DynamicModelProvider<ReleasesModel> provideReleasesFrom(ReleaseData... stemcellData) {
return new BoshCommandReleasesProvider(cliConfig) {
@Override
protected String executeCommand(ExternalCommand command) throws Exception {
String rows = mapper.writeValueAsString(stemcellData);
return "{\n" +
" \"Tables\": [\n" +
" {\n" +
" \"Rows\": " + rows +
" }\n" +
" ]\n" +
"}";
}
};
}
@Test public void contentAssistVMtype() throws Exception {
Editor editor = harness.newEditor(
"name: foo\n" +

View File

@@ -20,11 +20,11 @@ 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.CloudConfigModel;
import org.springframework.ide.vscode.bosh.mocks.MockCloudConfigProvider;
import org.springframework.ide.vscode.bosh.models.DynamicModelProvider;
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
@SuppressWarnings("unchecked")
public class BoshLanguageServerTest {
public static File getTestResource(String name) throws URISyntaxException {
@@ -36,7 +36,7 @@ public class BoshLanguageServerTest {
@Test
public void createAndInitializeServerWithWorkspace() throws Exception {
LanguageServerHarness harness = new LanguageServerHarness(() ->
new BoshLanguageServer(cliConfig, new MockCloudConfigProvider(cliConfig), mock(DynamicModelProvider.class))
new BoshLanguageServer(cliConfig, new MockCloudConfigProvider(cliConfig), mock(DynamicModelProvider.class), mock(DynamicModelProvider.class))
);
File workspaceRoot = getTestResource("/workspace/");
assertExpectedInitResult(harness.intialize(workspaceRoot));
@@ -46,7 +46,7 @@ public class BoshLanguageServerTest {
public void createAndInitializeServerWithoutWorkspace() throws Exception {
File workspaceRoot = null;
LanguageServerHarness harness = new LanguageServerHarness(() ->
new BoshLanguageServer(cliConfig, new MockCloudConfigProvider(cliConfig), Mockito.mock(DynamicModelProvider.class))
new BoshLanguageServer(cliConfig, new MockCloudConfigProvider(cliConfig), mock(DynamicModelProvider.class), mock(DynamicModelProvider.class))
);
assertExpectedInitResult(harness.intialize(workspaceRoot));
}

View File

@@ -0,0 +1,44 @@
/*******************************************************************************
* 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 static org.mockito.Mockito.mock;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.ide.vscode.bosh.BoshCliConfig;
import org.springframework.ide.vscode.commons.util.IOUtil;
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
import com.google.common.collect.ImmutableList;
public class BoshCommandReleasesProviderTest {
private static final String MOCK_DATA_RSRC = "/cmd-out/releases.json";
private BoshCliConfig cliConfig = new BoshCliConfig();
public BoshCommandReleasesProvider provider = Mockito.spy(new BoshCommandReleasesProvider(cliConfig));
@Before
public void setup() throws Exception {
Mockito.doReturn(IOUtil.toString(BoshCommandCloudConfigProviderTest.class.getResourceAsStream(MOCK_DATA_RSRC)))
.when(provider).executeCommand(Mockito.any());
}
@Test
public void getReleases() throws Exception {
assertEquals(ImmutableList.of(new ReleaseData("learn-bosh", "0+dev.2")),
provider.getModel(mock(DynamicSchemaContext.class)).getReleases()
);
}
}

View File

@@ -0,0 +1,28 @@
{
"Tables": [
{
"Content": "releases",
"Header": {
"commit_hash": "Commit Hash",
"name": "Name",
"version": "Version"
},
"Rows": [
{
"commit_hash": "5c612c4+",
"name": "learn-bosh",
"version": "0+dev.2"
}
],
"Notes": [
"(*) Currently deployed",
"(+) Uncommitted changes"
]
}
],
"Blocks": null,
"Lines": [
"Using environment '10.194.4.35' as client 'admin'",
"Succeeded"
]
}