diff --git a/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFStack.java b/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFStack.java new file mode 100644 index 000000000..8cb8b34d2 --- /dev/null +++ b/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFStack.java @@ -0,0 +1,15 @@ +/******************************************************************************* + * Copyright (c) 2016 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.commons.cloudfoundry.client; + +public interface CFStack extends CFEntity { + +} diff --git a/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/ClientRequests.java b/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/ClientRequests.java index 74e797579..7a4365a66 100644 --- a/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/ClientRequests.java +++ b/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/ClientRequests.java @@ -18,5 +18,6 @@ public interface ClientRequests { List getBuildpacks() throws Exception; List getServices() throws Exception; List getDomains() throws Exception; - + List getStacks() throws Exception; + } diff --git a/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/cftarget/CFTarget.java b/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/cftarget/CFTarget.java index c53a2feee..dae638aa0 100644 --- a/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/cftarget/CFTarget.java +++ b/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/cftarget/CFTarget.java @@ -17,6 +17,7 @@ import java.util.concurrent.TimeUnit; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFBuildpack; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFDomain; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFServiceInstance; +import org.springframework.ide.vscode.commons.cloudfoundry.client.CFStack; import org.springframework.ide.vscode.commons.cloudfoundry.client.ClientRequests; import com.google.common.cache.CacheBuilder; @@ -41,6 +42,7 @@ public class CFTarget { private LoadingCache> buildpacksCache; private LoadingCache> servicesCache; private LoadingCache> domainCache; + private LoadingCache> stacksCache; private CFCallableContext callableContext; public CFTarget(String targetName, CFClientParams params, ClientRequests requests, @@ -53,6 +55,20 @@ public class CFTarget { } private void initCache(ClientRequests requests) { + CacheLoader> stacksLoader = new CacheLoader>() { + + @Override + public List load(String key) throws Exception { + /* Cache of services does not use keys, as the whole cache + * gets wiped clean on any new call to CF. + */ + return runAndCheckForFailure(() -> requests.getStacks()); + } + }; + this.stacksCache = CacheBuilder.newBuilder() + .expireAfterAccess(CFTargetCache.SERVICES_EXPIRATION.toMillis(), TimeUnit.MILLISECONDS).build(stacksLoader); + + CacheLoader> servicesLoader = new CacheLoader>() { @Override @@ -64,7 +80,7 @@ public class CFTarget { } }; this.servicesCache = CacheBuilder.newBuilder() - .expireAfterAccess(CFTargetCache.SERVICES_EXPIRATION, TimeUnit.SECONDS).build(servicesLoader); + .expireAfterAccess(CFTargetCache.SERVICES_EXPIRATION.toMillis(), TimeUnit.MILLISECONDS).build(servicesLoader); CacheLoader> buildpacksLoader = new CacheLoader>() { @@ -77,7 +93,7 @@ public class CFTarget { } }; this.buildpacksCache = CacheBuilder.newBuilder() - .expireAfterAccess(CFTargetCache.TARGET_EXPIRATION, TimeUnit.HOURS).build(buildpacksLoader); + .expireAfterAccess(CFTargetCache.TARGET_EXPIRATION.toMillis(), TimeUnit.MILLISECONDS).build(buildpacksLoader); CacheLoader> domainLoader = new CacheLoader>() { @@ -88,7 +104,7 @@ public class CFTarget { }; this.domainCache = CacheBuilder.newBuilder() - .expireAfterAccess(CFTargetCache.TARGET_EXPIRATION, TimeUnit.HOURS).build(domainLoader); + .expireAfterAccess(CFTargetCache.TARGET_EXPIRATION.toMillis(), TimeUnit.MILLISECONDS).build(domainLoader); } protected T runAndCheckForFailure(Callable callable) throws Exception { @@ -103,6 +119,15 @@ public class CFTarget { return params; } + public List getStacks() throws Exception { + // Use the target name as the "key" , since Guava cache doesn't allow null keys + // However, the key is not really used when fetching buildpacks, as we are not caching + // buildpacks per target here. This class only represents ONE target, so it will only + // ever have one key + String key = getName(); + return this.stacksCache.get(key); + } + public List getBuildpacks() throws Exception { // Use the target name as the "key" , since Guava cache doesn't allow null keys // However, the key is not really used when fetching buildpacks, as we are not caching diff --git a/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/cftarget/CFTargetCache.java b/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/cftarget/CFTargetCache.java index 109495ddd..87f7c28d9 100644 --- a/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/cftarget/CFTargetCache.java +++ b/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/cftarget/CFTargetCache.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget; +import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; @@ -30,8 +31,8 @@ public class CFTargetCache { private final LoadingCache cache; private final CFCallableContext cacheCallableContext; - public static final long SERVICES_EXPIRATION = 10; - public static final long TARGET_EXPIRATION = 1; + public static final Duration SERVICES_EXPIRATION = Duration.ofSeconds(10); + public static final Duration TARGET_EXPIRATION = Duration.ofHours(1); public CFTargetCache(ClientParamsProvider paramsProvider, CloudFoundryClientFactory clientFactory, ClientTimeouts timeouts) { @@ -49,7 +50,7 @@ public class CFTargetCache { } }; - cache = CacheBuilder.newBuilder().maximumSize(1).expireAfterAccess(TARGET_EXPIRATION, TimeUnit.HOURS) + cache = CacheBuilder.newBuilder().maximumSize(1).expireAfterAccess(TARGET_EXPIRATION.toMillis(), TimeUnit.MILLISECONDS) .build(loader); } diff --git a/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/v2/CFWrappingV2.java b/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/v2/CFWrappingV2.java index b16829bb4..9461b7c45 100644 --- a/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/v2/CFWrappingV2.java +++ b/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/v2/CFWrappingV2.java @@ -14,10 +14,12 @@ import org.cloudfoundry.operations.buildpacks.Buildpack; import org.cloudfoundry.operations.domains.Domain; import org.cloudfoundry.operations.services.ServiceInstanceSummary; import org.cloudfoundry.operations.services.ServiceInstanceType; +import org.cloudfoundry.operations.stacks.Stack; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFBuildpack; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFDomain; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFEntities; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFServiceInstance; +import org.springframework.ide.vscode.commons.cloudfoundry.client.CFStack; /** * Various helper methods to 'wrap' objects returned by CF client into our own @@ -27,17 +29,16 @@ import org.springframework.ide.vscode.commons.cloudfoundry.client.CFServiceInsta */ public class CFWrappingV2 { - public static CFBuildpack wrap(Buildpack buildpack) { String name = buildpack.getName(); return CFEntities.createBuildpack(name); } - + public static CFDomain wrap(Domain domain) { String name = domain.getName(); return CFEntities.createDomain(name); } - + public static CFServiceInstance wrap(ServiceInstanceSummary serviceInstance) { String name = serviceInstance.getName(); String plan = serviceInstance.getPlan(); @@ -47,4 +48,22 @@ public class CFWrappingV2 { return CFEntities.createServiceInstance(name, service, plan); } + public static CFStack wrap(Stack stack) { + if (stack!=null) { + String name = stack.getName(); + return new CFStack() { + @Override + public String getName() { + return name; + } + + @Override + public String toString() { + return "CFStack("+name+")"; + } + }; + } + return null; + } + } diff --git a/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/v2/DefaultClientRequestsV2.java b/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/v2/DefaultClientRequestsV2.java index 30646d1d4..3461d8225 100644 --- a/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/v2/DefaultClientRequestsV2.java +++ b/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/v2/DefaultClientRequestsV2.java @@ -20,6 +20,7 @@ import org.cloudfoundry.operations.DefaultCloudFoundryOperations; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFBuildpack; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFDomain; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFServiceInstance; +import org.springframework.ide.vscode.commons.cloudfoundry.client.CFStack; import org.springframework.ide.vscode.commons.cloudfoundry.client.ClientRequests; import org.springframework.ide.vscode.commons.cloudfoundry.client.ClientTimeouts; import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.CFClientParams; @@ -83,7 +84,7 @@ public class DefaultClientRequestsV2 implements ClientRequests { ) ); } - + @Override public List getDomains() throws Exception { @@ -98,7 +99,7 @@ public class DefaultClientRequestsV2 implements ClientRequests { ) ); } - + @Override public List getBuildpacks() throws Exception { return ReactorUtils.get(timeouts.getBuildpacksTimeout(), CancelationTokens.NULL, @@ -112,6 +113,18 @@ public class DefaultClientRequestsV2 implements ClientRequests { ) ); } + @Override + public List getStacks() throws Exception { + return ReactorUtils.get( + log("operations.stacks().list()", + _operations.stacks() + .list() + .map(CFWrappingV2::wrap) + .collectList() + ) + ); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////// //// calls to client and operations with 'logging'. diff --git a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YTypeAssistContext.java b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YTypeAssistContext.java index e3518afc2..ef064892e 100644 --- a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YTypeAssistContext.java +++ b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/completion/YTypeAssistContext.java @@ -10,13 +10,14 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.yaml.completion; +import static org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal.DEEMP_EXISTS; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,6 +30,7 @@ import org.springframework.ide.vscode.commons.util.FuzzyMatcher; import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.Renderable; import org.springframework.ide.vscode.commons.util.ValueParseException; +import org.springframework.ide.vscode.commons.yaml.completion.DefaultCompletionFactory.ValueProposal; import org.springframework.ide.vscode.commons.yaml.hover.YPropertyInfoTemplates; import org.springframework.ide.vscode.commons.yaml.path.YamlPath; import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment; @@ -42,16 +44,11 @@ import org.springframework.ide.vscode.commons.yaml.schema.YValueHint; import org.springframework.ide.vscode.commons.yaml.structure.YamlDocument; import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SChildBearingNode; import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SNode; -import org.springframework.ide.vscode.commons.yaml.util.Streams; import org.springframework.ide.vscode.commons.yaml.util.YamlIndentUtil; -import org.springframework.ide.vscode.commons.yaml.completion.DefaultCompletionFactory.ValueProposal; - import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; -import static org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal.*; - public class YTypeAssistContext extends AbstractYamlAssistContext { final static Logger logger = LoggerFactory.getLogger(YTypeAssistContext.class); diff --git a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/AbstractCFHintsProvider.java b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/AbstractCFHintsProvider.java index 82f3a9a64..0f27a07ae 100644 --- a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/AbstractCFHintsProvider.java +++ b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/AbstractCFHintsProvider.java @@ -16,6 +16,7 @@ import java.util.concurrent.Callable; import java.util.logging.Level; import java.util.logging.Logger; +import org.springframework.ide.vscode.commons.cloudfoundry.client.CFServiceInstance; import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.CFTarget; import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.CFTargetCache; import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.ConnectionException; @@ -78,7 +79,7 @@ public abstract class AbstractCFHintsProvider implements Callable> buildPacksProvider = getBuildpacksProvider(); - Callable> servicesProvider = getServicesProvider(); - Callable> domainsProvider = getDomainsProvider(); + Callable> buildPacksProvider = new ManifestYamlCFBuildpacksProvider(getCfTargetCache()); + Callable> servicesProvider = new ManifestYamlCFServicesProvider(getCfTargetCache()); + Callable> domainsProvider = new ManifestYamlCFDomainsProvider(getCfTargetCache()); + Callable> stacksProvider = new ManifestYamlStacksProvider(getCfTargetCache()); return new ManifestYmlHintProviders() { @@ -115,6 +116,11 @@ public class ManifestYamlLanguageServer extends SimpleLanguageServer { public Callable> getBuildpackProviders() { return buildPacksProvider; } + + @Override + public Callable> getStacksProvider() { + return stacksProvider; + } }; } @@ -127,16 +133,4 @@ public class ManifestYamlLanguageServer extends SimpleLanguageServer { return cfTargetCache; } - private Callable> getBuildpacksProvider() { - return new ManifestYamlCFBuildpacksProvider(getCfTargetCache()); - } - - private Callable> getServicesProvider() { - return new ManifestYamlCFServicesProvider(getCfTargetCache()); - } - - private Callable> getDomainsProvider() { - return new ManifestYamlCFDomainsProvider(getCfTargetCache()); - } - } diff --git a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlSchemaProblemsTypes.java b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlSchemaProblemsTypes.java index 477c8ad40..807f01e83 100644 --- a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlSchemaProblemsTypes.java +++ b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlSchemaProblemsTypes.java @@ -15,9 +15,6 @@ import static org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaPr import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity; import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; -/** - * - */ public class ManifestYamlSchemaProblemsTypes { public static final ProblemType UNKNOWN_SERVICES_PROBLEM = problemType("UnknownServicesProblem", @@ -25,5 +22,8 @@ public class ManifestYamlSchemaProblemsTypes { public static final ProblemType UNKNOWN_DOMAIN_PROBLEM = problemType("UnknownDomainProblem", ProblemSeverity.WARNING); - + + public static final ProblemType UNKNOWN_STACK_PROBLEM = problemType("UnknownStackProblem", + ProblemSeverity.WARNING);; + } diff --git a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlStacksProvider.java b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlStacksProvider.java new file mode 100644 index 000000000..c89c3b8d1 --- /dev/null +++ b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlStacksProvider.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.manifest.yaml; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.springframework.ide.vscode.commons.cloudfoundry.client.CFStack; +import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.CFTarget; +import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.CFTargetCache; +import org.springframework.ide.vscode.commons.util.Renderable; +import org.springframework.ide.vscode.commons.util.Renderables; +import org.springframework.ide.vscode.commons.yaml.schema.BasicYValueHint; +import org.springframework.ide.vscode.commons.yaml.schema.YValueHint; + +public class ManifestYamlStacksProvider extends AbstractCFHintsProvider { + + public ManifestYamlStacksProvider(CFTargetCache targetCache) { + super(targetCache); + } + + @Override + protected String getTypeName() { + return "Stack"; + } + + @Override + protected Collection getHints(List targets) throws Exception { + // NOTE: empty list of services is a VALID result. A CF target may have + // no service instances + // created, so if empty list is returned from the client, then RETURN empty list. don't + // return null + // for empty services cases + List hints = new ArrayList<>(); + + for (CFTarget cfTarget : targets) { + List stacks = cfTarget.getStacks(); + Renderable targetLabel = Renderables.text(cfTarget.getLabel()); + if (stacks != null && !stacks.isEmpty()) { + for (CFStack s : stacks) { + String name = s.getName(); + String label = name; + YValueHint hint = new BasicYValueHint(name, label) + .setDocumentation(targetLabel); + if (!hints.contains(hint)) { + hints.add(hint); + } + } + return hints; + } + } + + return hints; + } + + +} diff --git a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlHintProviders.java b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlHintProviders.java index 09558ba5b..3a89b5f45 100644 --- a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlHintProviders.java +++ b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlHintProviders.java @@ -23,4 +23,6 @@ public interface ManifestYmlHintProviders { Callable> getDomainsProvider(); + Callable> getStacksProvider(); + } diff --git a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java index 70f4277d6..529509145 100644 --- a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java +++ b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java @@ -14,6 +14,8 @@ import java.util.Collection; import java.util.Set; import java.util.concurrent.Callable; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileException; +import org.springframework.ide.vscode.commons.util.EnumValueParser; import org.springframework.ide.vscode.commons.util.IntegerRange; import org.springframework.ide.vscode.commons.util.Renderable; import org.springframework.ide.vscode.commons.util.Renderables; @@ -37,7 +39,6 @@ public class ManifestYmlSchema implements YamlSchema { private final AbstractType TOPLEVEL_TYPE; private final YTypeUtil TYPE_UTIL; - private final Callable> buildpackProvider; private static final Set TOPLEVEL_EXCLUDED = ImmutableSet.of( "name", "host", "hosts" @@ -50,9 +51,10 @@ public class ManifestYmlSchema implements YamlSchema { public ManifestYmlSchema(ManifestYmlHintProviders providers) { - this.buildpackProvider = providers.getBuildpackProviders(); + Callable> buildpackProvider = providers.getBuildpackProviders(); Callable> servicesProvider = providers.getServicesProvider(); Callable> domainsProvider = providers.getDomainsProvider(); + Callable> stacksProvider = providers.getStacksProvider(); YTypeFactory f = new YTypeFactory(); @@ -65,11 +67,20 @@ public class ManifestYmlSchema implements YamlSchema { YAtomicType t_path = f.yatomic("Path"); YAtomicType t_buildpack = f.yatomic("Buildpack"); - if (this.buildpackProvider != null) { - t_buildpack.addHintProvider(this.buildpackProvider); + if (buildpackProvider != null) { + t_buildpack.addHintProvider(buildpackProvider); // t_buildpack.parseWith(ManifestYmlValueParsers.fromHints(t_buildpack.toString(), buildpackProvider)); } + YAtomicType t_stack = f.yatomic("Stack"); + t_stack.addHintProvider(stacksProvider); + t_stack.parseWith(new EnumValueParser(t_stack.toString(), YTypeFactory.valuesFromHintProvider(stacksProvider)) { + @Override + protected Exception errorOnParse(String message) { + return new ReconcileException(message, ManifestYamlSchemaProblemsTypes.UNKNOWN_STACK_PROBLEM); + } + }); + YAtomicType t_domain = f.yatomic("Domain"); if (domainsProvider != null) { @@ -136,7 +147,7 @@ public class ManifestYmlSchema implements YamlSchema { f.yprop("random-route", t_boolean), f.yprop("routes", f.yseq(route)), f.yprop("services", f.yseq(t_service)), - f.yprop("stack", t_string), + f.yprop("stack", t_stack), f.yprop("timeout", t_pos_integer), f.yprop("health-check-type", t_health_check_type), f.yprop("health-check-http-endpoint", t_ne_string) diff --git a/headless-services/manifest-yaml-language-server/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java b/headless-services/manifest-yaml-language-server/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java index 7f2e44941..b621f0e04 100644 --- a/headless-services/manifest-yaml-language-server/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java +++ b/headless-services/manifest-yaml-language-server/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java @@ -29,6 +29,7 @@ import org.mockito.Mockito; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFBuildpack; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFDomain; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFServiceInstance; +import org.springframework.ide.vscode.commons.cloudfoundry.client.CFStack; import org.springframework.ide.vscode.commons.cloudfoundry.client.ClientRequests; import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.NoTargetsException; import org.springframework.ide.vscode.languageserver.testharness.CodeAction; @@ -589,6 +590,51 @@ public class ManifestYamlEditorTest { editor.assertNoHover("otherdomain.org"); } + @Test public void stacksCompletion() throws Exception { + List stacks = ImmutableList.of( + mockStack("linux"), mockStack("windows") + ); + when(cloudfoundry.client.getStacks()).thenReturn(stacks); + Editor editor = harness.newEditor( + "stack: <*>" + ); + CompletionItem c = editor.assertCompletions( + "stack: linux<*>", + "stack: windows<*>" + ).get(0); + + assertEquals("an-org : a-space [test.io]", c.getDocumentation()); + } + + @Test public void stacksReconcile() throws Exception { + List stacks = ImmutableList.of( + mockStack("linux"), mockStack("windows") + ); + when(cloudfoundry.client.getStacks()).thenReturn(stacks); + { + Editor editor = harness.newEditor( + "stack: android<*>" + ); + Diagnostic p = editor.assertProblems("android|'android' is an unknown 'Stack'. Valid values are: [linux, windows]").get(0); + assertEquals(DiagnosticSeverity.Warning, p.getSeverity()); + } + + { + Editor editor = harness.newEditor( + "stack: <*>" + ); + Diagnostic p = editor.assertProblems("|'Stack' cannot be blank").get(0); + assertEquals(DiagnosticSeverity.Error, p.getSeverity()); + } + + } + + private CFStack mockStack(String name) { + CFStack stack = Mockito.mock(CFStack.class); + when(stack.getName()).thenReturn(name); + return stack; + } + @Test public void reconcileDuplicateKeys() throws Exception { Editor editor = harness.newEditor( @@ -882,10 +928,12 @@ public class ManifestYamlEditorTest { ClientRequests cfClient = cloudfoundry.client; when(cfClient.getBuildpacks()).thenThrow(new IOException("Can't get buildpacks")); when(cfClient.getServices()).thenThrow(new IOException("Can't get services")); + when(cfClient.getStacks()).thenThrow(new IOException("Can't get stacks")); Editor editor = harness.newEditor( "applications:\n" + "- name: foo\n" + " buildpack: bad-buildpack\n" + + " stack: bad-stack\n" + " services:\n" + " - bad-service\n" + " bogus: bad" //a token error to make sure reconciler is actually running! diff --git a/headless-services/manifest-yaml-language-server/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchemaTest.java b/headless-services/manifest-yaml-language-server/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchemaTest.java index 7d0364a02..31ca4e5ff 100644 --- a/headless-services/manifest-yaml-language-server/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchemaTest.java +++ b/headless-services/manifest-yaml-language-server/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchemaTest.java @@ -174,5 +174,10 @@ public class ManifestYmlSchemaTest { public Callable> getBuildpackProviders() { return null; } + + @Override + public Callable> getStacksProvider() { + return null; + } }; }