support for multi-item catalog yaml adds many tests, and the rest of the features - template and policy and location; and source yaml.
also a few significant REST API changes: * /v1/catalog/create API change returns a map (breaking) * catalog items include more information for entity and policies Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/eaaca787 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/eaaca787 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/eaaca787 Branch: refs/heads/master Commit: eaaca7874bc092acc5abac848eba30648836fd53 Parents: 08cb3e7 Author: Alex Heneveld <[email protected]> Authored: Fri Apr 3 13:29:28 2015 -0400 Committer: Alex Heneveld <[email protected]> Committed: Thu Apr 16 01:25:39 2015 -0500 ---------------------------------------------------------------------- .../java/brooklyn/catalog/BrooklynCatalog.java | 4 + .../java/brooklyn/location/LocationSpec.java | 9 +- .../brooklyn/catalog/CatalogPredicates.java | 12 +- .../catalog/internal/BasicBrooklynCatalog.java | 214 +++++++++++++++---- .../brooklyn/catalog/internal/CatalogUtils.java | 6 + .../location/basic/BasicLocationRegistry.java | 2 +- .../location/basic/CatalogLocationResolver.java | 3 +- .../brooklyn/camp/lite/CampYamlLiteTest.java | 6 +- .../entity/rebind/RebindCatalogItemTest.java | 10 +- docs/guide/misc/release-notes.md | 4 + .../camp/brooklyn/AbstractYamlTest.java | 2 +- .../CatalogOsgiVersionMoreEntityTest.java | 11 +- .../brooklyn/catalog/CatalogYamlEntityTest.java | 103 ++++++++- .../catalog/CatalogYamlLocationTest.java | 124 +++++++++-- .../brooklyn/catalog/CatalogYamlPolicyTest.java | 70 +++++- .../catalog/CatalogYamlTemplateTest.java | 77 +++++++ .../src/main/webapp/assets/js/view/catalog.js | 57 +++-- .../assets/tpl/catalog/add-catalog-entry.html | 4 +- .../webapp/assets/tpl/catalog/add-entity.html | 30 --- .../webapp/assets/tpl/catalog/add-yaml.html | 30 +++ .../main/java/brooklyn/rest/api/CatalogApi.java | 19 +- .../java/brooklyn/rest/api/LocationApi.java | 5 +- .../rest/domain/CatalogEntitySummary.java | 6 + .../rest/domain/CatalogItemSummary.java | 3 + .../rest/domain/CatalogPolicySummary.java | 3 + .../rest/resources/CatalogResource.java | 66 +++--- .../rest/resources/LocationResource.java | 4 +- .../rest/transform/CatalogTransformer.java | 44 +++- .../rest/resources/CatalogResourceTest.java | 67 +++--- .../src/main/java/brooklyn/util/yaml/Yamls.java | 12 ++ 30 files changed, 786 insertions(+), 221 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java b/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java index ce4386f..5b11cc6 100644 --- a/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java +++ b/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java @@ -90,7 +90,9 @@ public interface BrooklynCatalog { * Fails if the same version exists in catalog. * * @throws IllegalArgumentException if the yaml was invalid + * @deprecated since 0.7.0 use {@link #addItems(String, boolean)} */ + @Deprecated CatalogItem<?,?> addItem(String yaml); /** @@ -100,7 +102,9 @@ public interface BrooklynCatalog { * item exists with the same symbolicName and version * * @throws IllegalArgumentException if the yaml was invalid + * @deprecated since 0.7.0 use {@link #addItems(String, boolean)} */ + @Deprecated CatalogItem<?,?> addItem(String yaml, boolean forceUpdate); /** http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/api/src/main/java/brooklyn/location/LocationSpec.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/brooklyn/location/LocationSpec.java b/api/src/main/java/brooklyn/location/LocationSpec.java index 7e3c4f9..10a6d7e 100644 --- a/api/src/main/java/brooklyn/location/LocationSpec.java +++ b/api/src/main/java/brooklyn/location/LocationSpec.java @@ -78,8 +78,11 @@ public class LocationSpec<T extends Location> extends AbstractBrooklynObjectSpec * original entity spec. */ public static <T extends Location> LocationSpec<T> create(LocationSpec<T> spec) { - // FIXME Why can I not use LocationSpec<T>? - LocationSpec<?> result = create(spec.getType()) + // need this to get LocationSpec<T> rather than LocationSpec<? extends T> + @SuppressWarnings("unchecked") + Class<T> exactType = (Class<T>)spec.getType(); + + LocationSpec<T> result = create(exactType) .displayName(spec.getDisplayName()) .tags(spec.getTags()) .configure(spec.getConfig()) @@ -176,7 +179,7 @@ public class LocationSpec<T extends Location> extends AbstractBrooklynObjectSpec return this; } - @SuppressWarnings("unchecked") + @SuppressWarnings({ "unchecked", "rawtypes" }) public <E> LocationSpec<T> extensions(Map<Class<?>, ?> extensions) { for (Map.Entry<Class<?>, ?> entry : extensions.entrySet()) { extension((Class)entry.getKey(), entry.getValue()); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/core/src/main/java/brooklyn/catalog/CatalogPredicates.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/catalog/CatalogPredicates.java b/core/src/main/java/brooklyn/catalog/CatalogPredicates.java index 4331a1a..2ffebdf 100644 --- a/core/src/main/java/brooklyn/catalog/CatalogPredicates.java +++ b/core/src/main/java/brooklyn/catalog/CatalogPredicates.java @@ -21,6 +21,7 @@ package brooklyn.catalog; import javax.annotation.Nullable; import brooklyn.catalog.CatalogItem.CatalogItemType; +import brooklyn.catalog.internal.CatalogUtils; import brooklyn.entity.Application; import brooklyn.entity.Entity; import brooklyn.entity.proxying.EntitySpec; @@ -126,5 +127,14 @@ public class CatalogPredicates { } }; } - + + public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> isBestVersion(final ManagementContext mgmt) { + return new Predicate<CatalogItem<T,SpecT>>() { + @Override + public boolean apply(@Nullable CatalogItem<T,SpecT> item) { + return CatalogUtils.isBestVersion(mgmt, item); + } + }; + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java index 8a562d0..09ef02f 100644 --- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java +++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java @@ -72,6 +72,7 @@ import brooklyn.util.text.Strings; import brooklyn.util.time.Duration; import brooklyn.util.time.Time; import brooklyn.util.yaml.Yamls; +import brooklyn.util.yaml.Yamls.YamlExtract; import com.google.common.base.Function; import com.google.common.base.Predicate; @@ -322,6 +323,11 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { String yaml = loadedItem.getPlanYaml(); if (yaml!=null) { + // preferred way is to parse the yaml, to resolve references late; + // the parsing on load is to populate some fields, but it is optional. + // TODO messy for location and policy that we need brooklyn.{locations,policies} root of the yaml, but it works; + // see related comment when the yaml is set, in addAbstractCatalogItems + // (not sure if anywhere else relies on that syntax; if not, it should be easy to fix!) DeploymentPlan plan = makePlanFromYaml(yaml); BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt, item); SpecT spec; @@ -384,7 +390,6 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { } } - @SuppressWarnings("unchecked") private <T, SpecT> SpecT createPolicySpec(DeploymentPlan plan, BrooklynClassLoadingContext loader) { //Would ideally re-use io.brooklyn.camp.brooklyn.spi.creation.BrooklynEntityDecorationResolver.PolicySpecResolver //but it is CAMP specific and there is no easy way to get hold of it. @@ -395,6 +400,11 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { Object policy = Iterables.getOnlyElement((Iterable<?>)policies); + return createPolicySpec(loader, policy); + } + + @SuppressWarnings("unchecked") + private <T, SpecT> SpecT createPolicySpec(BrooklynClassLoadingContext loader, Object policy) { Map<String, Object> config; if (policy instanceof String) { config = ImmutableMap.<String, Object>of("type", policy); @@ -413,7 +423,6 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { return (SpecT) spec; } - @SuppressWarnings("unchecked") private <T, SpecT> SpecT createLocationSpec(DeploymentPlan plan, BrooklynClassLoadingContext loader) { // See #createPolicySpec; this impl is modeled on that. // spec.catalogItemId is set by caller @@ -424,6 +433,11 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { Object location = Iterables.getOnlyElement((Iterable<?>)locations); + return createLocationSpec(loader, location); + } + + @SuppressWarnings("unchecked") + private <T, SpecT> SpecT createLocationSpec(BrooklynClassLoadingContext loader, Object location) { Map<String, Object> config; if (location instanceof String) { config = ImmutableMap.<String, Object>of("type", location); @@ -457,7 +471,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { } else { throw new IllegalStateException("No class or resolver found for location type "+type); } - } + } } @SuppressWarnings("unchecked") @@ -509,7 +523,9 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { } if (foundKey==null) return Maybe.absent("Missing entry '"+firstKey+"'"); value = map.get(foundKey); - if (!type.isInstance(value)) return Maybe.absent("Entry for '"+firstKey+"' should be of type "+type+", not "+value.getClass()); + if (type.equals(String.class) && Number.class.isInstance(value)) value = value.toString(); + if (!type.isInstance(value)) + throw new IllegalArgumentException("Entry for '"+firstKey+"' should be of type "+type+", not "+value.getClass()); return Maybe.of((T)value); } @@ -518,7 +534,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { return (Maybe<Map<?,?>>)(Maybe) getFirstAs(map, Map.class, firstKey, otherKeys); } - private List<CatalogItemDtoAbstract<?,?>> getAbstractCatalogItems(String yaml) { + private List<CatalogItemDtoAbstract<?,?>> addAbstractCatalogItems(String yaml, Boolean whenAddingAsDtoShouldWeForce) { Map<?,?> itemDef = Yamls.getAs(Yamls.parseAll(yaml), Map.class); Map<?,?> catalogMetadata = getFirstAsMap(itemDef, "brooklyn.catalog", "catalog").orNull(); if (catalogMetadata==null) @@ -527,30 +543,36 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { List<CatalogItemDtoAbstract<?, ?>> result = MutableList.of(); - addAbstractCatalogItems(catalogMetadata, result, null); + addAbstractCatalogItems(Yamls.getTextOfYamlAtPath(yaml, "brooklyn.catalog").getMatchedYamlTextOrWarn(), + catalogMetadata, result, null, whenAddingAsDtoShouldWeForce); itemDef.remove("brooklyn.catalog"); catalogMetadata.remove("item"); catalogMetadata.remove("items"); if (!itemDef.isEmpty()) { - log.debug("Reading brooklyn.catalog peer keys as item"); + log.debug("Reading brooklyn.catalog peer keys as item ('top-level syntax')"); Map<String,?> rootItem = MutableMap.of("item", itemDef); - addAbstractCatalogItems(rootItem, result, catalogMetadata); + String rootItemYaml = yaml; + YamlExtract yamlExtract = Yamls.getTextOfYamlAtPath(rootItemYaml, "brooklyn.catalog"); + String match = yamlExtract.withOriginalIndentation(true).withKeyIncluded(true).getMatchedYamlTextOrWarn(); + if (match!=null) { + if (rootItemYaml.startsWith(match)) rootItemYaml = Strings.removeFromStart(rootItemYaml, match); + else rootItemYaml = Strings.replaceAllNonRegex(rootItemYaml, "\n"+match, ""); + } + addAbstractCatalogItems("item:\n"+makeAsIndentedObject(rootItemYaml), rootItem, result, catalogMetadata, whenAddingAsDtoShouldWeForce); } return result; } - enum CatalogItemTypes { ENTITY, TEMPLATE, POLICY, LOCATION } - @SuppressWarnings("unchecked") - private void addAbstractCatalogItems(Map<?,?> itemMetadata, List<CatalogItemDtoAbstract<?, ?>> result, Map<?,?> parentMetadata) { + private void addAbstractCatalogItems(String sourceYaml, Map<?,?> itemMetadata, List<CatalogItemDtoAbstract<?, ?>> result, Map<?,?> parentMetadata, Boolean whenAddingAsDtoShouldWeForce) { + + if (sourceYaml==null) sourceYaml = new Yaml().dump(itemMetadata); // TODO: -// get yaml -// (tests) -// docs used as test casts -- (doc assertions that these match -- or import -- known test casesyamls) -// multiple versions in catalog page +// docs used as test cases -- (doc assertions that these match -- or import -- known test cases yamls) +// multiple versions in web app root Map<Object,Object> catalogMetadata = MutableMap.builder().putAll(parentMetadata).putAll(itemMetadata).build(); @@ -565,16 +587,22 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { Object item = catalogMetadata.remove("item"); if (items!=null) { + int count = 0; for (Map<?,?> i: ((List<Map<?,?>>)items)) { - addAbstractCatalogItems(i, result, catalogMetadata); + addAbstractCatalogItems(Yamls.getTextOfYamlAtPath(sourceYaml, "items", count).getMatchedYamlTextOrWarn(), + i, result, catalogMetadata, whenAddingAsDtoShouldWeForce); + count++; } } if (item==null) return; + + // now look at the actual item, first correcting the sourceYaml and interpreting the catalog metadata + String itemYaml = Yamls.getTextOfYamlAtPath(sourceYaml, "item").getMatchedYamlTextOrWarn(); + if (itemYaml!=null) sourceYaml = itemYaml; + else sourceYaml = new Yaml().dump(item); - //now parse the metadata and apply to item - - CatalogItemTypes itemType = TypeCoercions.coerce(getFirstAs(catalogMetadata, String.class, "item.type", "itemType", "item_type").or(CatalogItemTypes.ENTITY.toString()), CatalogItemTypes.class); + CatalogItemType itemType = TypeCoercions.coerce(getFirstAs(catalogMetadata, Object.class, "item.type", "itemType", "item_type").orNull(), CatalogItemType.class); String id = getFirstAs(catalogMetadata, String.class, "id").orNull(); String version = getFirstAs(catalogMetadata, String.class, "version").orNull(); @@ -587,19 +615,25 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { Strings.isNonBlank(name) && !name.equals(displayName)) { log.warn("Name property will be ignored due to the existence of displayName and at least one of id, symbolicName"); } - - // TODO use src yaml if avail - String yaml = new Yaml().dump(item); - + DeploymentPlan plan = null; try { - plan = makePlanFromYaml(yaml); + plan = makePlanFromYaml(sourceYaml); } catch (Exception e) { Exceptions.propagateIfFatal(e); - if (itemType==CatalogItemTypes.ENTITY || itemType==CatalogItemTypes.TEMPLATE) - log.warn("Could not parse item YAML for "+itemType+" (registering anyway): "+e+"\n"+yaml); + if (itemType==CatalogItemType.ENTITY || itemType==CatalogItemType.TEMPLATE) + log.warn("Could not parse item YAML for "+itemType+" (registering anyway): "+e+"\n"+sourceYaml); } + if (Strings.isBlank(id)) { + // let ID be inferred, especially from name, to support style where only "name" is specified, with inline version + if (Strings.isNonBlank(symbolicName) && Strings.isNonBlank(version)) { + id = symbolicName + ":" + version; + } else if (Strings.isNonBlank(name)) { + id = name; + } + } + final String catalogSymbolicName; if (Strings.isNonBlank(symbolicName)) { catalogSymbolicName = symbolicName; @@ -609,8 +643,6 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { } else { catalogSymbolicName = id; } - } else if (Strings.isNonBlank(name)) { - catalogSymbolicName = name; } else if (plan!=null && Strings.isNonBlank(plan.getName())) { catalogSymbolicName = plan.getName(); } else if (plan!=null && plan.getServices().size()==1) { @@ -620,8 +652,8 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { } catalogSymbolicName = svc.getServiceType(); } else { - log.error("Can't infer catalog item symbolicName from the following plan:\n" + yaml); - throw new IllegalStateException("Can't infer catalog item symbolicName from catalog item description"); + log.error("Can't infer catalog item symbolicName from the following plan:\n" + sourceYaml); + throw new IllegalStateException("Can't infer catalog item symbolicName from catalog item metadata"); } final String catalogVersion; @@ -668,23 +700,103 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { String versionedId = CatalogUtils.getVersionedId(catalogSymbolicName, catalogVersion!=null ? catalogVersion : NO_VERSION); BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt, versionedId, libraries); - // TODO for entities, we parse. for templates we don't. (?) - AbstractBrooklynObjectSpec<?, ?> spec = createSpec(catalogSymbolicName, plan, loader); + CatalogItemType inferredItemType = inferType(plan); + boolean usePlan; + if (inferredItemType!=null) { + if (itemType!=null) { + if (itemType==CatalogItemType.TEMPLATE && inferredItemType==CatalogItemType.ENTITY) { + // template - we use the plan, but coupled with the type to make a catalog template item + usePlan = true; + } else if (itemType==inferredItemType) { + // if the plan showed the type, it is either a parsed entity or a legacy spec type + usePlan = true; + if (itemType==CatalogItemType.TEMPLATE || itemType==CatalogItemType.ENTITY) { + // normal + } else { + log.warn("Legacy syntax used for "+itemType+" "+catalogSymbolicName+": should declare item.type and specify type"); + } + } else { + throw new IllegalStateException("Explicit type "+itemType+" "+catalogSymbolicName+" does not match blueprint inferred type "+inferredItemType); + } + } else { + // no type was declared, use the inferred type from the plan + itemType = inferredItemType; + usePlan = true; + } + } else if (itemType!=null) { + if (itemType==CatalogItemType.TEMPLATE || itemType==CatalogItemType.ENTITY) { + log.warn("Incomplete plan detected for "+itemType+" "+catalogSymbolicName+"; will likely fail subsequently"); + usePlan = true; + } else { + usePlan = false; + } + } else { + throw new IllegalStateException("Unable to detect type for "+catalogSymbolicName+"; error in catalog metadata or blueprint"); + } + + AbstractBrooklynObjectSpec<?, ?> spec; + if (usePlan) { + spec = createSpecFromPlan(catalogSymbolicName, plan, loader); + } else { + String key; + switch (itemType) { + // we don't actually need the spec here, since the yaml is what is used at load time, + // but it isn't a bad idea to confirm that it resolves + case POLICY: + spec = createPolicySpec(loader, item); + key = POLICIES_KEY; + break; + case LOCATION: + spec = createLocationSpec(loader, item); + key = LOCATIONS_KEY; + break; + default: throw new IllegalStateException("Cannot create "+itemType+" "+catalogSymbolicName+"; invalid metadata or blueprint"); + } + // TODO currently we then convert yaml to legacy brooklyn.{location,policy} syntax for subsequent usage; + // would be better to (in the other path) convert to {type: ..., brooklyn.config: ... } format and expect that elsewhere + sourceYaml = key + ":\n" + makeAsIndentedList(sourceYaml); + } - CatalogItemDtoAbstract<?, ?> dto = createItemBuilder(spec, catalogSymbolicName, catalogVersion) + CatalogItemDtoAbstract<?, ?> dto = createItemBuilder(itemType, spec, catalogSymbolicName, catalogVersion) .libraries(libraries) .displayName(catalogDisplayName) .description(catalogDescription) .deprecated(catalogDeprecated) .iconUrl(catalogIconUrl) - .plan(yaml) + .plan(sourceYaml) .build(); dto.setManagementContext((ManagementContextInternal) mgmt); + if (whenAddingAsDtoShouldWeForce!=null) { + addItemDto(dto, whenAddingAsDtoShouldWeForce); + } result.add(dto); } - private AbstractBrooklynObjectSpec<?,?> createSpec(String symbolicName, DeploymentPlan plan, BrooklynClassLoadingContext loader) { + private String makeAsIndentedList(String yaml) { + String[] lines = yaml.split("\n"); + lines[0] = "- "+lines[0]; + for (int i=1; i<lines.length; i++) + lines[i] = " " + lines[i]; + return Strings.join(lines, "\n"); + } + + private String makeAsIndentedObject(String yaml) { + String[] lines = yaml.split("\n"); + for (int i=0; i<lines.length; i++) + lines[i] = " " + lines[i]; + return Strings.join(lines, "\n"); + } + + private CatalogItemType inferType(DeploymentPlan plan) { + if (plan==null) return null; + if (isEntityPlan(plan)) return CatalogItemType.ENTITY; + if (isPolicyPlan(plan)) return CatalogItemType.POLICY; + if (isLocationPlan(plan)) return CatalogItemType.LOCATION; + return null; + } + + private AbstractBrooklynObjectSpec<?,?> createSpecFromPlan(String symbolicName, DeploymentPlan plan, BrooklynClassLoadingContext loader) { if (isPolicyPlan(plan)) { return createPolicySpec(plan, loader); } else if (isLocationPlan(plan)) { @@ -694,7 +806,20 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { } } - private CatalogItemBuilder<?> createItemBuilder(AbstractBrooklynObjectSpec<?, ?> spec, String itemId, String version) { + private CatalogItemBuilder<?> createItemBuilder(CatalogItemType itemType, AbstractBrooklynObjectSpec<?, ?> spec, String itemId, String version) { + if (itemType!=null) { + switch (itemType) { + case ENTITY: return CatalogItemBuilder.newEntity(itemId, version); + case TEMPLATE: return CatalogItemBuilder.newTemplate(itemId, version); + case POLICY: return CatalogItemBuilder.newPolicy(itemId, version); + case LOCATION: return CatalogItemBuilder.newLocation(itemId, version); + } + log.warn("Legacy syntax for "+itemId+"; unexpected item.type "+itemType); + } else { + log.warn("Legacy syntax for "+itemId+"; no item.type declared or inferred"); + } + + // @deprecated - should not come here if (spec instanceof EntitySpec) { if (isApplicationSpec((EntitySpec<?>)spec)) { return CatalogItemBuilder.newTemplate(itemId, version); @@ -714,12 +839,16 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { return !Boolean.TRUE.equals(spec.getConfig().get(EntityManagementUtils.WRAPPER_APP_MARKER)); } + private boolean isEntityPlan(DeploymentPlan plan) { + return plan!=null && !plan.getServices().isEmpty() || !plan.getArtifacts().isEmpty(); + } + private boolean isPolicyPlan(DeploymentPlan plan) { - return plan.getCustomAttributes().containsKey(POLICIES_KEY); + return !isEntityPlan(plan) && plan.getCustomAttributes().containsKey(POLICIES_KEY); } private boolean isLocationPlan(DeploymentPlan plan) { - return plan.getCustomAttributes().containsKey(LOCATIONS_KEY); + return !isEntityPlan(plan) && plan.getCustomAttributes().containsKey(LOCATIONS_KEY); } private DeploymentPlan makePlanFromYaml(String yaml) { @@ -746,10 +875,11 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { public List<? extends CatalogItem<?,?>> addItems(String yaml, boolean forceUpdate) { log.debug("Adding manual catalog item to "+mgmt+": "+yaml); checkNotNull(yaml, "yaml"); - List<CatalogItemDtoAbstract<?, ?>> result = getAbstractCatalogItems(yaml); - for (CatalogItemDtoAbstract<?, ?> item: result) { - addItemDto(item, forceUpdate); - } + List<CatalogItemDtoAbstract<?, ?>> result = addAbstractCatalogItems(yaml, forceUpdate); + // previously we did this here, but now we do it on each item, in case #2 refers to #1 +// for (CatalogItemDtoAbstract<?, ?> item: result) { +// addItemDto(item, forceUpdate); +// } return result; } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java index 50ef436..c72fc9b 100644 --- a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java +++ b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java @@ -218,6 +218,12 @@ public class CatalogUtils { } } + public static boolean isBestVersion(ManagementContext mgmt, CatalogItem<?,?> item) { + CatalogItem<?, ?> bestVersion = getCatalogItemOptionalVersion(mgmt, item.getSymbolicName()); + if (bestVersion==null) return false; + return (bestVersion.getVersion().equals(item.getVersion())); + } + public static <T,SpecT> CatalogItem<T, SpecT> getCatalogItemOptionalVersion(ManagementContext mgmt, Class<T> type, String versionedId) { if (looksLikeVersionedId(versionedId)) { String id = getIdFromVersionedId(versionedId); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/core/src/main/java/brooklyn/location/basic/BasicLocationRegistry.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/location/basic/BasicLocationRegistry.java b/core/src/main/java/brooklyn/location/basic/BasicLocationRegistry.java index f0b466e..c59298b 100644 --- a/core/src/main/java/brooklyn/location/basic/BasicLocationRegistry.java +++ b/core/src/main/java/brooklyn/location/basic/BasicLocationRegistry.java @@ -84,7 +84,7 @@ import com.google.common.collect.Sets; * it goes through the following steps: * * <ol> - * <li>Call {@link BrooklynCatalog#addItem(String)} + * <li>Call {@link BrooklynCatalog#addItems(String)} * <ol> * <li>This automatically calls {@link #updateDefinedLocation(CatalogItem)} * <li>A LocationDefinition is creating, using as its id the {@link CatalogItem#getSymbolicName()}. http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/core/src/main/java/brooklyn/location/basic/CatalogLocationResolver.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/location/basic/CatalogLocationResolver.java b/core/src/main/java/brooklyn/location/basic/CatalogLocationResolver.java index 4823b05..591873d 100644 --- a/core/src/main/java/brooklyn/location/basic/CatalogLocationResolver.java +++ b/core/src/main/java/brooklyn/location/basic/CatalogLocationResolver.java @@ -39,6 +39,7 @@ import brooklyn.management.ManagementContext; */ public class CatalogLocationResolver implements LocationResolver { + @SuppressWarnings("unused") private static final Logger log = LoggerFactory.getLogger(CatalogLocationResolver.class); public static final String NAME = "brooklyn.catalog"; @@ -51,7 +52,7 @@ public class CatalogLocationResolver implements LocationResolver { } @Override - @SuppressWarnings({ "rawtypes" }) + @SuppressWarnings({ "rawtypes", "unchecked" }) public Location newLocationFromString(Map locationFlags, String spec, brooklyn.location.LocationRegistry registry) { String id = spec.substring(NAME.length()+1); CatalogItem<?, ?> item = CatalogUtils.getCatalogItemOptionalVersion(managementContext, id); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java index b9b4bca..2739c79 100644 --- a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java +++ b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java @@ -156,7 +156,7 @@ public class CampYamlLiteTest { public void testYamlServiceForCatalog() { TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH); - CatalogItem<?, ?> realItem = mgmt.getCatalog().addItem(Streams.readFullyString(getClass().getResourceAsStream("test-app-service-blueprint.yaml"))); + CatalogItem<?, ?> realItem = Iterables.getOnlyElement(mgmt.getCatalog().addItems(Streams.readFullyString(getClass().getResourceAsStream("test-app-service-blueprint.yaml")))); Iterable<CatalogItem<Object, Object>> retrievedItems = mgmt.getCatalog() .getCatalogItems(CatalogPredicates.symbolicName(Predicates.equalTo("catalog-name"))); @@ -185,7 +185,7 @@ public class CampYamlLiteTest { String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL; String yaml = getSampleMyCatalogAppYaml(symbolicName, bundleUrl); - mgmt.getCatalog().addItem(yaml); + mgmt.getCatalog().addItems(yaml); assertMgmtHasSampleMyCatalogApp(symbolicName, bundleUrl); } @@ -203,7 +203,7 @@ public class CampYamlLiteTest { CampPlatformWithJustBrooklynMgmt platform2 = new CampPlatformWithJustBrooklynMgmt(mgmt2); MockWebPlatform.populate(platform2, TestAppAssemblyInstantiator.class); - mgmt2.getCatalog().addItem(yaml); + mgmt2.getCatalog().addItems(yaml); String xml = ((BasicBrooklynCatalog) mgmt2.getCatalog()).toXmlString(); ((BasicBrooklynCatalog) mgmt.getCatalog()).reset(CatalogDto.newDtoFromXmlContents(xml, "copy of temporary catalog")); } finally { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java index 4036dc5..e0260c3 100644 --- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java +++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java @@ -212,8 +212,9 @@ public class RebindCatalogItemTest extends RebindTestFixtureWithApp { " version: " + TEST_VERSION + "\n" + "services:\n" + "- type: io.camp.mock:AppServer"; + addItem(origManagementContext, yaml); + BasicBrooklynCatalog catalog = (BasicBrooklynCatalog) origManagementContext.getCatalog(); - catalog.addItem(yaml); String catalogXml = catalog.toXmlString(); catalog.reset(CatalogDto.newDtoFromXmlContents(catalogXml, "Test reset")); rebindAndAssertCatalogsAreEqual(); @@ -227,9 +228,10 @@ public class RebindCatalogItemTest extends RebindTestFixtureWithApp { " version: " + TEST_VERSION + "\n" + "services:\n" + "- type: io.camp.mock:AppServer"; - BasicBrooklynCatalog catalog = (BasicBrooklynCatalog) origManagementContext.getCatalog(); - CatalogItem<?, ?> catalogItem = catalog.addItem(yaml); + CatalogItem<?, ?> catalogItem = addItem(origManagementContext, yaml); assertNotNull(catalogItem, "catalogItem"); + BasicBrooklynCatalog catalog = (BasicBrooklynCatalog) origManagementContext.getCatalog(); + catalogItem.setDeprecated(true); catalog.persist(catalogItem); rebindAndAssertCatalogsAreEqual(); @@ -238,7 +240,7 @@ public class RebindCatalogItemTest extends RebindTestFixtureWithApp { } protected CatalogItem<?, ?> addItem(ManagementContext mgmt, String yaml) { - CatalogItem<?, ?> added = mgmt.getCatalog().addItem(yaml); + CatalogItem<?, ?> added = Iterables.getOnlyElement(mgmt.getCatalog().addItems(yaml)); LOG.info("Added item to catalog: {}, id={}", added, added.getId()); assertCatalogContains(mgmt.getCatalog(), added); return added; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/docs/guide/misc/release-notes.md ---------------------------------------------------------------------- diff --git a/docs/guide/misc/release-notes.md b/docs/guide/misc/release-notes.md index 2b6dadf..1a324d6 100644 --- a/docs/guide/misc/release-notes.md +++ b/docs/guide/misc/release-notes.md @@ -47,6 +47,10 @@ For more information, please visit [brooklyn.io](http://brooklyn.io). * If `brooklyn.webconsole.security.https.required=true` is specified with no explicit port, it now defaults to 8443; previously it would default to 8081 even in the case of `https`. +* The /v1/catalog/create method now returns a map of ID to item map, instead of an item map, + as the call supports multiple items defined in the YAML. + + ### Community Activity Brooklyn has moved into the Apache Software Foundation. http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java ---------------------------------------------------------------------- diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java index dd7ad58..b1e6817 100644 --- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java +++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java @@ -153,7 +153,7 @@ public abstract class AbstractYamlTest { } protected void addCatalogItem(String catalogYaml) { - mgmt().getCatalog().addItem(catalogYaml, forceUpdate); + mgmt().getCatalog().addItems(catalogYaml, forceUpdate); } protected void deleteCatalogEntity(String catalogItem) { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java ---------------------------------------------------------------------- diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java index 3527a5d..19c9ef8 100644 --- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java +++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java @@ -18,16 +18,19 @@ */ package io.brooklyn.camp.brooklyn.catalog; -import brooklyn.test.TestResourceUnavailableException; import io.brooklyn.camp.brooklyn.AbstractYamlTest; import io.brooklyn.camp.brooklyn.spi.creation.BrooklynEntityMatcher; import org.testng.Assert; import org.testng.annotations.Test; +import brooklyn.catalog.CatalogItem; +import brooklyn.catalog.CatalogItem.CatalogItemType; +import brooklyn.catalog.internal.CatalogUtils; import brooklyn.entity.Entity; import brooklyn.management.osgi.OsgiVersionMoreEntityTest; import brooklyn.policy.Policy; +import brooklyn.test.TestResourceUnavailableException; import brooklyn.util.ResourceUtils; import com.google.common.collect.Iterables; @@ -45,6 +48,12 @@ public class CatalogOsgiVersionMoreEntityTest extends AbstractYamlTest { TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), "/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.1.0.jar"); addCatalogItem(getLocalResource("more-entity-v1-osgi-catalog.yaml")); + CatalogItem<?, ?> item = CatalogUtils.getCatalogItemOptionalVersion(mgmt(), "more-entity"); + Assert.assertNotNull(item); + Assert.assertEquals(item.getVersion(), "1.0"); + Assert.assertEquals(item.getCatalogItemType(), CatalogItemType.ENTITY); + Assert.assertEquals(item.getLibraries().size(), 1); + Entity app = createAndStartApplication("services: [ { type: 'more-entity:1.0' } ]"); Entity moreEntity = Iterables.getOnlyElement(app.getChildren()); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java ---------------------------------------------------------------------- diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java index 5b79982..1737718 100644 --- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java +++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java @@ -25,6 +25,7 @@ import io.brooklyn.camp.brooklyn.AbstractYamlTest; import java.io.InputStream; import java.util.Collection; +import java.util.List; import org.testng.Assert; import org.testng.annotations.Test; @@ -38,6 +39,7 @@ import brooklyn.management.osgi.OsgiStandaloneTest; import brooklyn.management.osgi.OsgiTestResources; import brooklyn.test.TestResourceUnavailableException; import brooklyn.util.ResourceUtils; +import brooklyn.util.collections.MutableList; import com.google.common.collect.Iterables; @@ -59,6 +61,30 @@ public class CatalogYamlEntityTest extends AbstractYamlTest { } @Test + public void testAddCatalogItemAsSiblingOfCatalogMetadata() throws Exception { + TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH); + + String symbolicName = "my.catalog.app.id.load"; + addCatalogItem( + "brooklyn.catalog:", + " id: " + symbolicName, + " name: My Catalog App", + " description: My description", + " icon_url: classpath://path/to/myicon.jpg", + " version: " + TEST_VERSION, + " libraries:", + " - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL, + "", + "services:", + "- type: " + SIMPLE_ENTITY_TYPE); + + CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(symbolicName, TEST_VERSION); + assertEquals(item.getSymbolicName(), symbolicName); + + deleteCatalogEntity(symbolicName); + } + + @Test public void testAddCatalogItemWithoutVersion() throws Exception { TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH); @@ -76,6 +102,23 @@ public class CatalogYamlEntityTest extends AbstractYamlTest { } @Test + public void testAddCatalogItemWithInlinedVersion() throws Exception { + TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH); + + String id = "inline_version.app"; + addCatalogItem( + "brooklyn.catalog:", + " name: " + id+":"+TEST_VERSION, + " libraries:", + " - " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL, + "services:", + "- type: " + SIMPLE_ENTITY_TYPE); + CatalogItem<?, ?> catalogItem = mgmt().getCatalog().getCatalogItem(id, TEST_VERSION); + assertEquals(catalogItem.getVersion(), TEST_VERSION); + mgmt().getCatalog().deleteCatalogItem(id, TEST_VERSION); + } + + @Test public void testLaunchApplicationReferencingCatalog() throws Exception { TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH); @@ -84,7 +127,7 @@ public class CatalogYamlEntityTest extends AbstractYamlTest { } @Test - public void testLaunchApplicationUnverionedCatalogReference() throws Exception { + public void testLaunchApplicationUnversionedCatalogReference() throws Exception { TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH); String symbolicName = "my.catalog.app.id.fail"; @@ -106,6 +149,27 @@ public class CatalogYamlEntityTest extends AbstractYamlTest { String referencedSymbolicName = "my.catalog.app.id.referenced"; String referrerSymbolicName = "my.catalog.app.id.referring"; + addCatalogOSGiEntities(referencedSymbolicName, SIMPLE_ENTITY_TYPE, referrerSymbolicName, ver(referencedSymbolicName)); + + String yaml = "name: simple-app-yaml\n" + + "location: localhost\n" + + "services: \n" + + " - serviceType: " + ver(referrerSymbolicName); + Entity app = createAndStartApplication(yaml); + + Entity simpleEntity = Iterables.getOnlyElement(app.getChildren()); + assertEquals(simpleEntity.getEntityType().getName(), SIMPLE_ENTITY_TYPE); + + deleteCatalogEntity(referencedSymbolicName); + deleteCatalogEntity(referrerSymbolicName); + } + + @Test + public void testLaunchApplicationWithCatalogReferencingOtherCatalogInTwoSteps() throws Exception { + TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH); + + String referencedSymbolicName = "my.catalog.app.id.referenced"; + String referrerSymbolicName = "my.catalog.app.id.referring"; addCatalogOSGiEntity(referencedSymbolicName, SIMPLE_ENTITY_TYPE); addCatalogOSGiEntity(referrerSymbolicName, ver(referencedSymbolicName)); @@ -465,11 +529,32 @@ public class CatalogYamlEntityTest extends AbstractYamlTest { " version: " + TEST_VERSION, " libraries:", " - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL, - "", - "services:", - "- type: " + serviceType); + " item:", + " services:", + " - type: " + serviceType); } + private void addCatalogOSGiEntities(String ...namesAndTypes) { + List<String> lines = MutableList.of( + "brooklyn.catalog:", + " name: My Catalog App", + " description: My description", + " icon_url: classpath://path/to/myicon.jpg", + " version: " + TEST_VERSION, + " libraries:", + " - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL, + " items:"); + + for (int i=0; i<namesAndTypes.length; i+=2) { + lines.addAll(MutableList.of( + " - id: " + namesAndTypes[i], + " item:", + " services:", + " - type: " + namesAndTypes[i+1])); + } + + addCatalogItem(lines); + } private void addCatalogChildOSGiEntity(String symbolicName, String serviceType) { addCatalogItem( "brooklyn.catalog:", @@ -480,11 +565,11 @@ public class CatalogYamlEntityTest extends AbstractYamlTest { " version: " + TEST_VERSION, " libraries:", " - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL, - "", - "services:", - "- type: " + BasicEntity.class.getName(), - " brooklyn.children:", - " - type: " + serviceType); + " item:", + " services:", + " - type: " + BasicEntity.class.getName(), + " brooklyn.children:", + " - type: " + serviceType); } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java ---------------------------------------------------------------------- diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java index ef5aa0f..c0d41d2 100644 --- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java +++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java @@ -22,16 +22,20 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; import io.brooklyn.camp.brooklyn.AbstractYamlTest; +import java.util.Collection; import java.util.List; +import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import brooklyn.catalog.CatalogItem; +import brooklyn.catalog.CatalogItem.CatalogBundle; import brooklyn.catalog.CatalogPredicates; import brooklyn.entity.Entity; import brooklyn.event.basic.BasicConfigKey; import brooklyn.location.Location; import brooklyn.location.LocationDefinition; +import brooklyn.location.LocationSpec; import brooklyn.location.basic.LocalhostMachineProvisioningLocation; import brooklyn.management.osgi.OsgiStandaloneTest; import brooklyn.test.TestResourceUnavailableException; @@ -46,13 +50,63 @@ public class CatalogYamlLocationTest extends AbstractYamlTest { private static final String LOCALHOST_LOCATION_TYPE = LocalhostMachineProvisioningLocation.class.getName(); private static final String SIMPLE_LOCATION_TYPE = "brooklyn.osgi.tests.SimpleLocation"; + @AfterMethod + public void tearDown() { + for (CatalogItem<Location, LocationSpec<?>> ci : mgmt().getCatalog().getCatalogItems(CatalogPredicates.IS_LOCATION)) { + mgmt().getCatalog().deleteCatalogItem(ci.getSymbolicName(), ci.getVersion()); + } + } + @Test public void testAddCatalogItem() throws Exception { assertEquals(countCatalogLocations(), 0); String symbolicName = "my.catalog.location.id.load"; - addCatalogOSGiLocation(symbolicName, SIMPLE_LOCATION_TYPE); + addCatalogLocation(symbolicName, LOCALHOST_LOCATION_TYPE, null); + assertAdded(symbolicName, LOCALHOST_LOCATION_TYPE); + removeAndAssert(symbolicName); + } + + @Test + public void testAddCatalogItemOsgi() throws Exception { + assertEquals(countCatalogLocations(), 0); + + String symbolicName = "my.catalog.location.id.load"; + addCatalogLocation(symbolicName, SIMPLE_LOCATION_TYPE, getOsgiLibraries()); + assertAdded(symbolicName, SIMPLE_LOCATION_TYPE); + assertOsgi(symbolicName); + removeAndAssert(symbolicName); + } + + @Test + public void testAddCatalogItemTopLevelItemSyntax() throws Exception { + assertEquals(countCatalogLocations(), 0); + + String symbolicName = "my.catalog.location.id.load"; + addCatalogLocationTopLevelItemSyntax(symbolicName, LOCALHOST_LOCATION_TYPE, null); + assertAdded(symbolicName, LOCALHOST_LOCATION_TYPE); + removeAndAssert(symbolicName); + } + + @Test + public void testAddCatalogItemOsgiTopLevelItemSyntax() throws Exception { + assertEquals(countCatalogLocations(), 0); + String symbolicName = "my.catalog.location.id.load"; + addCatalogLocationTopLevelItemSyntax(symbolicName, SIMPLE_LOCATION_TYPE, getOsgiLibraries()); + assertAdded(symbolicName, SIMPLE_LOCATION_TYPE); + assertOsgi(symbolicName); + removeAndAssert(symbolicName); + } + + private void assertOsgi(String symbolicName) { + CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(symbolicName, TEST_VERSION); + Collection<CatalogBundle> libs = item.getLibraries(); + assertEquals(libs.size(), 1); + assertEquals(Iterables.getOnlyElement(libs).getUrl(), Iterables.getOnlyElement(getOsgiLibraries())); + } + + private void assertAdded(String symbolicName, String expectedJavaType) { CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(symbolicName, TEST_VERSION); assertEquals(item.getSymbolicName(), symbolicName); assertEquals(countCatalogLocations(), 1); @@ -61,7 +115,12 @@ public class CatalogYamlLocationTest extends AbstractYamlTest { LocationDefinition def = mgmt().getLocationRegistry().getDefinedLocationByName(symbolicName); assertEquals(def.getId(), symbolicName); assertEquals(def.getName(), symbolicName); - + + LocationSpec<?> spec = (LocationSpec<?>)mgmt().getCatalog().createSpec(item); + assertEquals(spec.getType().getName(), expectedJavaType); + } + + private void removeAndAssert(String symbolicName) { // Deleting item: should be gone from catalog, and from location registry deleteCatalogEntity(symbolicName); @@ -72,7 +131,7 @@ public class CatalogYamlLocationTest extends AbstractYamlTest { @Test public void testLaunchApplicationReferencingLocationClass() throws Exception { String symbolicName = "my.catalog.location.id.launch"; - addCatalogLocation(symbolicName, LOCALHOST_LOCATION_TYPE); + addCatalogLocation(symbolicName, LOCALHOST_LOCATION_TYPE, null); runLaunchApplicationReferencingLocation(symbolicName, LOCALHOST_LOCATION_TYPE); deleteCatalogEntity(symbolicName); @@ -81,7 +140,25 @@ public class CatalogYamlLocationTest extends AbstractYamlTest { @Test public void testLaunchApplicationReferencingLocationSpec() throws Exception { String symbolicName = "my.catalog.location.id.launch"; - addCatalogLocation(symbolicName, LOCALHOST_LOCATION_SPEC); + addCatalogLocation(symbolicName, LOCALHOST_LOCATION_SPEC, null); + runLaunchApplicationReferencingLocation(symbolicName, LOCALHOST_LOCATION_TYPE); + + deleteCatalogEntity(symbolicName); + } + + @Test + public void testLaunchApplicationReferencingLocationClassTopLevelItemSyntax() throws Exception { + String symbolicName = "my.catalog.location.id.launch"; + addCatalogLocationTopLevelItemSyntax(symbolicName, LOCALHOST_LOCATION_TYPE, null); + runLaunchApplicationReferencingLocation(symbolicName, LOCALHOST_LOCATION_TYPE); + + deleteCatalogEntity(symbolicName); + } + + @Test + public void testLaunchApplicationReferencingLocationSpecTopLevelSyntax() throws Exception { + String symbolicName = "my.catalog.location.id.launch"; + addCatalogLocationTopLevelItemSyntax(symbolicName, LOCALHOST_LOCATION_SPEC, null); runLaunchApplicationReferencingLocation(symbolicName, LOCALHOST_LOCATION_TYPE); deleteCatalogEntity(symbolicName); @@ -90,7 +167,7 @@ public class CatalogYamlLocationTest extends AbstractYamlTest { @Test public void testLaunchApplicationReferencingOsgiLocation() throws Exception { String symbolicName = "my.catalog.location.id.launch"; - addCatalogOSGiLocation(symbolicName, SIMPLE_LOCATION_TYPE); + addCatalogLocation(symbolicName, SIMPLE_LOCATION_TYPE, getOsgiLibraries()); runLaunchApplicationReferencingLocation(symbolicName, SIMPLE_LOCATION_TYPE); deleteCatalogEntity(symbolicName); @@ -114,30 +191,49 @@ public class CatalogYamlLocationTest extends AbstractYamlTest { assertEquals(location.getConfig(new BasicConfigKey<String>(String.class, "config3")), "config3"); } - private void addCatalogLocation(String symbolicName, String serviceType) { - addCatalogLocation(symbolicName, serviceType, ImmutableList.<String>of()); - } - - private void addCatalogOSGiLocation(String symbolicName, String serviceType) { + private List<String> getOsgiLibraries() { TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH); - addCatalogLocation(symbolicName, serviceType, ImmutableList.of(OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL)); + return ImmutableList.of(OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL); } - private void addCatalogLocation(String symbolicName, String serviceType, List<String> libraries) { + private void addCatalogLocation(String symbolicName, String locationType, List<String> libraries) { + ImmutableList.Builder<String> yaml = ImmutableList.<String>builder().add( + "brooklyn.catalog:", + " id: " + symbolicName, + " name: My Catalog Location", + " description: My description", + " version: " + TEST_VERSION); + if (libraries!=null && libraries.size() > 0) { + yaml.add(" libraries:") + .addAll(Lists.transform(libraries, StringFunctions.prepend(" - url: "))); + } + yaml.add( + " item.type: location", + " item:", + " type: " + locationType, + " brooklyn.config:", + " config1: config1", + " config2: config2"); + + + addCatalogItem(yaml.build()); + } + + private void addCatalogLocationTopLevelItemSyntax(String symbolicName, String locationType, List<String> libraries) { ImmutableList.Builder<String> yaml = ImmutableList.<String>builder().add( "brooklyn.catalog:", " id: " + symbolicName, " name: My Catalog Location", " description: My description", " version: " + TEST_VERSION); - if (libraries.size() > 0) { + if (libraries!=null && libraries.size() > 0) { yaml.add(" libraries:") .addAll(Lists.transform(libraries, StringFunctions.prepend(" - url: "))); } yaml.add( "", "brooklyn.locations:", - "- type: " + serviceType, + "- type: " + locationType, " brooklyn.config:", " config1: config1", " config2: config2"); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java ---------------------------------------------------------------------- diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java index d83c11a..b7370be 100644 --- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java +++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java @@ -19,8 +19,6 @@ package io.brooklyn.camp.brooklyn.catalog; import static org.testng.Assert.assertEquals; - -import brooklyn.test.TestResourceUnavailableException; import io.brooklyn.camp.brooklyn.AbstractYamlTest; import org.testng.annotations.Test; @@ -31,6 +29,7 @@ import brooklyn.entity.Entity; import brooklyn.event.basic.BasicConfigKey; import brooklyn.management.osgi.OsgiStandaloneTest; import brooklyn.policy.Policy; +import brooklyn.test.TestResourceUnavailableException; import com.google.common.collect.Iterables; @@ -43,7 +42,21 @@ public class CatalogYamlPolicyTest extends AbstractYamlTest { assertEquals(countCatalogPolicies(), 0); String symbolicName = "my.catalog.policy.id.load"; - addCatalogOSGiPolicy(symbolicName, SIMPLE_POLICY_TYPE); + addCatalogOsgiPolicy(symbolicName, SIMPLE_POLICY_TYPE); + + CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(symbolicName, TEST_VERSION); + assertEquals(item.getSymbolicName(), symbolicName); + assertEquals(countCatalogPolicies(), 1); + + deleteCatalogEntity(symbolicName); + } + + @Test + public void testAddCatalogItemTopLevelSyntax() throws Exception { + assertEquals(countCatalogPolicies(), 0); + + String symbolicName = "my.catalog.policy.id.load"; + addCatalogOsgiPolicyTopLevelSyntax(symbolicName, SIMPLE_POLICY_TYPE); CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(symbolicName, TEST_VERSION); assertEquals(item.getSymbolicName(), symbolicName); @@ -55,7 +68,7 @@ public class CatalogYamlPolicyTest extends AbstractYamlTest { @Test public void testLaunchApplicationReferencingPolicy() throws Exception { String symbolicName = "my.catalog.policy.id.launch"; - addCatalogOSGiPolicy(symbolicName, SIMPLE_POLICY_TYPE); + addCatalogOsgiPolicy(symbolicName, SIMPLE_POLICY_TYPE); Entity app = createAndStartApplication( "name: simple-app-yaml", "location: localhost", @@ -78,10 +91,35 @@ public class CatalogYamlPolicyTest extends AbstractYamlTest { } @Test + public void testLaunchApplicationReferencingPolicyTopLevelSyntax() throws Exception { + String symbolicName = "my.catalog.policy.id.launch"; + addCatalogOsgiPolicyTopLevelSyntax(symbolicName, SIMPLE_POLICY_TYPE); + Entity app = createAndStartApplication( + "name: simple-app-yaml", + "location: localhost", + "services: ", + " - type: brooklyn.entity.basic.BasicEntity\n" + + " brooklyn.policies:\n" + + " - type: " + ver(symbolicName), + " brooklyn.config:", + " config2: config2 override", + " config3: config3"); + + Entity simpleEntity = Iterables.getOnlyElement(app.getChildren()); + Policy policy = Iterables.getOnlyElement(simpleEntity.getPolicies()); + assertEquals(policy.getPolicyType().getName(), SIMPLE_POLICY_TYPE); + assertEquals(policy.getConfig(new BasicConfigKey<String>(String.class, "config1")), "config1"); + assertEquals(policy.getConfig(new BasicConfigKey<String>(String.class, "config2")), "config2 override"); + assertEquals(policy.getConfig(new BasicConfigKey<String>(String.class, "config3")), "config3"); + + deleteCatalogEntity(symbolicName); + } + + @Test public void testLaunchApplicationWithCatalogReferencingOtherCatalog() throws Exception { String referencedSymbolicName = "my.catalog.policy.id.referenced"; String referrerSymbolicName = "my.catalog.policy.id.referring"; - addCatalogOSGiPolicy(referencedSymbolicName, SIMPLE_POLICY_TYPE); + addCatalogOsgiPolicy(referencedSymbolicName, SIMPLE_POLICY_TYPE); addCatalogItem( "brooklyn.catalog:", @@ -112,7 +150,27 @@ public class CatalogYamlPolicyTest extends AbstractYamlTest { deleteCatalogEntity(referencedSymbolicName); } - private void addCatalogOSGiPolicy(String symbolicName, String serviceType) { + private void addCatalogOsgiPolicy(String symbolicName, String serviceType) { + TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH); + + addCatalogItem( + "brooklyn.catalog:", + " id: " + symbolicName, + " name: My Catalog Policy", + " description: My description", + " icon_url: classpath://path/to/myicon.jpg", + " version: " + TEST_VERSION, + " libraries:", + " - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL, + " item_type: policy", + " item:", + " type: " + serviceType, + " brooklyn.config:", + " config1: config1", + " config2: config2"); + } + + private void addCatalogOsgiPolicyTopLevelSyntax(String symbolicName, String serviceType) { TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH); addCatalogItem( http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java ---------------------------------------------------------------------- diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java new file mode 100644 index 0000000..e473d2d --- /dev/null +++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.brooklyn.camp.brooklyn.catalog; + +import static org.testng.Assert.assertEquals; +import io.brooklyn.camp.brooklyn.AbstractYamlTest; + +import org.testng.Assert; +import org.testng.TestListenerAdapter; +import org.testng.TestNG; +import org.testng.annotations.Test; + +import brooklyn.catalog.CatalogItem; +import brooklyn.catalog.CatalogItem.CatalogItemType; +import brooklyn.management.osgi.OsgiStandaloneTest; +import brooklyn.management.osgi.OsgiTestResources; +import brooklyn.test.TestResourceUnavailableException; + + +public class CatalogYamlTemplateTest extends AbstractYamlTest { + + private static final String SIMPLE_ENTITY_TYPE = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY; + + @Test + public void testAddCatalogItem() throws Exception { + TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH); + + addCatalogItem( + "brooklyn.catalog:", + " id: t1", + " item_type: template", + " name: My Catalog App", + " description: My description", + " icon_url: classpath://path/to/myicon.jpg", + " version: " + TEST_VERSION, + " libraries:", + " - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL, + " item:", + " services:", + " # this sample comment should be included", + " - type: " + SIMPLE_ENTITY_TYPE); + + CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem("t1", TEST_VERSION); + assertEquals(item.getCatalogItemType(), CatalogItemType.TEMPLATE); + Assert.assertTrue(item.getPlanYaml().indexOf("sample comment")>=0, + "YAML did not include original comments; it was:\n"+item.getPlanYaml()); + Assert.assertFalse(item.getPlanYaml().indexOf("description")>=0, + "YAML included metadata which should have been excluded; it was:\n"+item.getPlanYaml()); + + deleteCatalogEntity("t1"); + } + + // convenience for running in eclipse when the TestNG plugin drags in old version of snake yaml + public static void main(String[] args) { + TestListenerAdapter tla = new TestListenerAdapter(); + TestNG testng = new TestNG(); + testng.setTestClasses(new Class[] { CatalogYamlTemplateTest.class }); + testng.addListener(tla); + testng.run(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/usage/jsgui/src/main/webapp/assets/js/view/catalog.js ---------------------------------------------------------------------- diff --git a/usage/jsgui/src/main/webapp/assets/js/view/catalog.js b/usage/jsgui/src/main/webapp/assets/js/view/catalog.js index 6f75136..68b3c6f 100644 --- a/usage/jsgui/src/main/webapp/assets/js/view/catalog.js +++ b/usage/jsgui/src/main/webapp/assets/js/view/catalog.js @@ -24,7 +24,7 @@ define([ "text!tpl/catalog/details-generic.html", "text!tpl/catalog/details-location.html", "text!tpl/catalog/add-catalog-entry.html", - "text!tpl/catalog/add-entity.html", + "text!tpl/catalog/add-yaml.html", "text!tpl/catalog/add-location.html", "text!tpl/catalog/nav-entry.html", @@ -32,7 +32,7 @@ define([ ], function(_, $, Backbone, Brooklyn, Location, Entity, CatalogPageHtml, DetailsEntityHtml, DetailsGenericHtml, LocationDetailsHtml, - AddCatalogEntryHtml, AddEntityHtml, AddLocationHtml, EntryHtml) { + AddCatalogEntryHtml, AddYamlHtml, AddLocationHtml, EntryHtml) { // Holds the currently active details type, e.g. applications, policies. Bit of a workaround // to share the active view with all instances of AccordionItemView, so clicking the 'reload @@ -130,15 +130,21 @@ define([ render: function (initialView) { this.$el.html(this.template()); if (initialView) { + if (initialView == "entity") initialView = "yaml"; + this.$("[data-context='"+initialView+"']").addClass("active"); this.showFormForType(initialView) } return this; }, + clearWithHtml: function(template) { + if (this.contextView) this.contextView.close(); + this.context = undefined; + this.$(".btn").removeClass("active"); + this.$("#catalog-add-form").html(template); + }, beforeClose: function () { - if (this.contextView) { - this.contextView.close(); - } + if (this.contextView) this.contextView.close(); }, showContext: function(event) { var $event = $(event.currentTarget); @@ -152,13 +158,13 @@ define([ }, showFormForType: function (type) { this.context = type; - if (type == "entity") { - this.contextView = newEntityForm(this.options.parent); + if (type == "yaml" || type == "entity") { + this.contextView = newYamlForm(this, this.options.parent); } else if (type == "location") { - this.contextView = newLocationForm(this.options.parent); + this.contextView = newLocationForm(this, this.options.parent); } else if (type !== undefined) { console.log("unknown catalog type " + type); - this.showFormForType("entity"); + this.showFormForType("yaml"); return; } Backbone.history.navigate("/v1/catalog/new/" + type); @@ -166,11 +172,10 @@ define([ } }); - function newEntityForm(parent) { + function newYamlForm(addView, addViewParent) { return new Brooklyn.view.Form({ - template: _.template(AddEntityHtml), + template: _.template(AddYamlHtml), onSubmit: function (model) { - console.log("Submit entity", model.get("yaml")); var submitButton = this.$(".catalog-submit-button"); // "loading" is an indicator to Bootstrap, not a string to display submitButton.button("loading"); @@ -185,9 +190,13 @@ define([ .done(function (data, status, xhr) { // Can extract location of new item with: //model.url = Brooklyn.util.pathOf(xhr.getResponseHeader("Location")); - self.close(); // one of the calls below should draw a different view - parent.loadAccordionItem("entities", data.id); - parent.loadAccordionItem("applications", data.id); + if (_.size(data)==0) { + addView.clearWithHtml( "No items supplied." ); + } else { + addView.clearWithHtml( "Added: "+_.escape(_.keys(data).join(", ")) + + (_.size(data)==1 ? ". Loading..." : "") ); + addViewParent.loadAnyAccordionItem(_.size(data)==1 ? _.keys(data)[0] : undefined); + } }) .fail(function (xhr, status, error) { submitButton.button("reset"); @@ -201,7 +210,7 @@ define([ } // Could adapt to edit existing locations too. - function newLocationForm(parent) { + function newLocationForm(addView, addViewParent) { // Renders with config key list var body = new (Backbone.View.extend({ beforeClose: function() { @@ -235,10 +244,9 @@ define([ submitButton.button("loading"); location.set("config", configKeys); location.save() - .done(function (newModel) { - newModel = new Location.Model(newModel); - self.close(); // the call below should draw a different view - parent.loadAccordionItem("locations", newModel.id); + .done(function (data) { + addView.clearWithHtml( "Added: "+data.id+". Loading..." ); + addViewParent.loadAccordionItem("locations", data.id); }) .fail(function (response) { submitButton.button("reset"); @@ -264,7 +272,7 @@ define([ this.model = model; }, url: function() { - return "/v1/catalog/" + this.name; + return "/v1/catalog/" + this.name+"?allVersions=true"; } }); @@ -509,6 +517,13 @@ define([ this.setDetailsView(newView); }, + loadAnyAccordionItem: function (id) { + this.loadAccordionItem("entities", id); + this.loadAccordionItem("applications", id); + this.loadAccordionItem("policies", id); + this.loadAccordionItem("locations", id); + }, + loadAccordionItem: function (kind, id) { if (!this.accordion[kind]) { console.error("No accordion for: " + kind); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-catalog-entry.html ---------------------------------------------------------------------- diff --git a/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-catalog-entry.html b/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-catalog-entry.html index 6bd8c00..238dd77 100644 --- a/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-catalog-entry.html +++ b/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-catalog-entry.html @@ -18,11 +18,11 @@ under the License. --> <div class="catalog-details"> - <h2>Add a new...</h2> + <h2>Add to Catalog</h2> <br/> <div data-toggle="buttons-radio"> - <button class="btn btn-large show-context" data-context="entity">Entity</button> + <button class="btn btn-large show-context" data-context="yaml">YAML</button> <button class="btn btn-large show-context" data-context="location">Location</button> </div> http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-entity.html ---------------------------------------------------------------------- diff --git a/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-entity.html b/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-entity.html deleted file mode 100644 index 9c64e90..0000000 --- a/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-entity.html +++ /dev/null @@ -1,30 +0,0 @@ -<!-- -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. See the License for the -specific language governing permissions and limitations -under the License. ---> -<form> - <label for="new-blueprint">Enter blueprint:</label> - <textarea id='new-blueprint' name='yaml' rows='5' cols='15'></textarea> - - <button class='catalog-submit-button btn' data-loading-text='Saving...'>Submit</button> - <p class="catalog-save-error hide"> - <span class="alert-error"> - <strong>Error:</strong><br/> - <span class="catalog-error-message"></span> - </span> - </p> -</form> http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-yaml.html ---------------------------------------------------------------------- diff --git a/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-yaml.html b/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-yaml.html new file mode 100644 index 0000000..9c64e90 --- /dev/null +++ b/usage/jsgui/src/main/webapp/assets/tpl/catalog/add-yaml.html @@ -0,0 +1,30 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +--> +<form> + <label for="new-blueprint">Enter blueprint:</label> + <textarea id='new-blueprint' name='yaml' rows='5' cols='15'></textarea> + + <button class='catalog-submit-button btn' data-loading-text='Saving...'>Submit</button> + <p class="catalog-save-error hide"> + <span class="alert-error"> + <strong>Error:</strong><br/> + <span class="catalog-error-message"></span> + </span> + </p> +</form> http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java ---------------------------------------------------------------------- diff --git a/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java b/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java index bf097e2..49549fd 100644 --- a/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java +++ b/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java @@ -64,7 +64,8 @@ public interface CatalogApi { @Consumes @POST - @ApiOperation(value = "Add a catalog item (e.g. new type of entity, policy or location) by uploading YAML descriptor", responseClass = "String") + @ApiOperation(value = "Add a catalog item (e.g. new type of entity, policy or location) by uploading YAML descriptor " + + "Return value is map of ID to CatalogItemSummary, with code 201 CREATED.", responseClass = "Response") public Response create( @ApiParam(name = "yaml", value = "YAML descriptor of catalog item", required = true) @Valid String yaml); @@ -135,7 +136,9 @@ public interface CatalogApi { @ApiParam(name = "regex", value = "Regular expression to search for") @QueryParam("regex") @DefaultValue("") String regex, @ApiParam(name = "fragment", value = "Substring case-insensitive to search for") - @QueryParam("fragment") @DefaultValue("") String fragment); + @QueryParam("fragment") @DefaultValue("") String fragment, + @ApiParam(name = "allVersions", value = "Include all versions (defaults false, only returning the best version)") + @QueryParam("allVersions") @DefaultValue("false") boolean includeAllVersions); @GET @Path("/applications") @@ -144,7 +147,9 @@ public interface CatalogApi { @ApiParam(name = "regex", value = "Regular expression to search for") @QueryParam("regex") @DefaultValue("") String regex, @ApiParam(name = "fragment", value = "Substring case-insensitive to search for") - @QueryParam("fragment") @DefaultValue("") String fragment); + @QueryParam("fragment") @DefaultValue("") String fragment, + @ApiParam(name = "allVersions", value = "Include all versions (defaults false, only returning the best version)") + @QueryParam("allVersions") @DefaultValue("false") boolean includeAllVersions); /** @deprecated since 0.7.0 use {@link #getEntity(String, String)} */ @Deprecated @@ -203,7 +208,9 @@ public interface CatalogApi { @ApiParam(name = "regex", value = "Regular expression to search for") @QueryParam("regex") @DefaultValue("") String regex, @ApiParam(name = "fragment", value = "Substring case-insensitive to search for") - @QueryParam("fragment") @DefaultValue("") String fragment); + @QueryParam("fragment") @DefaultValue("") String fragment, + @ApiParam(name = "allVersions", value = "Include all versions (defaults false, only returning the best version)") + @QueryParam("allVersions") @DefaultValue("false") boolean includeAllVersions); /** @deprecated since 0.7.0 use {@link #getPolicy(String, String)} */ @Deprecated @@ -236,7 +243,9 @@ public interface CatalogApi { @ApiParam(name = "regex", value = "Regular expression to search for") @QueryParam("regex") @DefaultValue("") String regex, @ApiParam(name = "fragment", value = "Substring case-insensitive to search for") - @QueryParam("fragment") @DefaultValue("") String fragment); + @QueryParam("fragment") @DefaultValue("") String fragment, + @ApiParam(name = "allVersions", value = "Include all versions (defaults false, only returning the best version)") + @QueryParam("allVersions") @DefaultValue("false") boolean includeAllVersions); /** @deprecated since 0.7.0 use {@link #getLocation(String, String)} */ @Deprecated http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/usage/rest-api/src/main/java/brooklyn/rest/api/LocationApi.java ---------------------------------------------------------------------- diff --git a/usage/rest-api/src/main/java/brooklyn/rest/api/LocationApi.java b/usage/rest-api/src/main/java/brooklyn/rest/api/LocationApi.java index a5c5207..5035c19 100644 --- a/usage/rest-api/src/main/java/brooklyn/rest/api/LocationApi.java +++ b/usage/rest-api/src/main/java/brooklyn/rest/api/LocationApi.java @@ -41,6 +41,7 @@ import brooklyn.rest.domain.LocationSummary; import com.wordnik.swagger.core.ApiOperation; import com.wordnik.swagger.core.ApiParam; +@SuppressWarnings("deprecation") @Path("/v1/locations") @Apidoc("Locations") @Produces(MediaType.APPLICATION_JSON) @@ -79,9 +80,7 @@ public interface LocationApi { @DefaultValue("false") @QueryParam("full") String fullConfig); - /** - * @deprecated since 0.7.0; use {@link CatalogApi#create(String)} - */ + /** @deprecated since 0.7.0 use {@link CatalogApi#create(String)} with a location definition */ @POST @ApiOperation(value = "Create a new location definition", responseClass = "String") @Deprecated http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogEntitySummary.java ---------------------------------------------------------------------- diff --git a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogEntitySummary.java b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogEntitySummary.java index c08e82a..268437a 100644 --- a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogEntitySummary.java +++ b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogEntitySummary.java @@ -23,13 +23,19 @@ import java.util.Map; import java.util.Set; import org.codehaus.jackson.annotate.JsonProperty; +import org.codehaus.jackson.map.annotate.JsonSerialize; +import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion; public class CatalogEntitySummary extends CatalogItemSummary { private static final long serialVersionUID = 1063908984191424539L; + @JsonSerialize(include=Inclusion.NON_EMPTY) private final Set<EntityConfigSummary> config; + + @JsonSerialize(include=Inclusion.NON_EMPTY) private final Set<SensorSummary> sensors; + @JsonSerialize(include=Inclusion.NON_EMPTY) private final Set<EffectorSummary> effectors; public CatalogEntitySummary( http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogItemSummary.java ---------------------------------------------------------------------- diff --git a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogItemSummary.java b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogItemSummary.java index db979e2..79b1d27 100644 --- a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogItemSummary.java +++ b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogItemSummary.java @@ -23,6 +23,7 @@ import java.net.URI; import java.util.Map; import org.apache.commons.lang.builder.EqualsBuilder; +import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion; @@ -32,6 +33,8 @@ import com.google.common.collect.ImmutableMap; /** variant of Catalog*ItemDto objects for JS/JSON serialization; * see also, subclasses */ +@JsonIgnoreProperties(ignoreUnknown = true) +// ignore unknown, ie properties from subclasses (entity) public class CatalogItemSummary implements HasId, HasName, Serializable { private static final long serialVersionUID = -823483595879417681L; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eaaca787/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java ---------------------------------------------------------------------- diff --git a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java index f30006a..75cc6ae 100644 --- a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java +++ b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java @@ -23,6 +23,8 @@ import java.util.Map; import java.util.Set; import org.codehaus.jackson.annotate.JsonProperty; +import org.codehaus.jackson.map.annotate.JsonSerialize; +import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion; import com.google.common.collect.ImmutableSet; @@ -30,6 +32,7 @@ public class CatalogPolicySummary extends CatalogItemSummary { private static final long serialVersionUID = -588856488327394445L; + @JsonSerialize(include=Inclusion.NON_EMPTY) private final Set<PolicyConfigSummary> config; public CatalogPolicySummary(
