From 2b5e55a0465071a0592889bfe24520d0440effa5 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Fri, 5 Jan 2018 12:24:36 +0000 Subject: [PATCH 1/2] rest tests for same types added --- .../resources/BundleAndTypeResourcesTest.java | 269 ++++++++++++++++++ 1 file changed, 269 insertions(+) diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeResourcesTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeResourcesTest.java index 87fa3782b1..3672a7a0ce 100644 --- a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeResourcesTest.java +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeResourcesTest.java @@ -71,6 +71,7 @@ import org.apache.brooklyn.util.collections.MutableSet; import org.apache.brooklyn.util.core.ResourceUtils; import org.apache.brooklyn.util.core.osgi.BundleMaker; +import org.apache.brooklyn.util.http.HttpAsserts; import org.apache.brooklyn.util.javalang.JavaClassNames; import org.apache.brooklyn.util.javalang.Reflections; import org.apache.brooklyn.util.os.Os; @@ -85,6 +86,7 @@ import org.testng.reporters.Files; import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; @@ -1095,4 +1097,271 @@ private boolean checkTraits(boolean currentExpectedToBeWorking) { return currentExpectedToBeWorking; } + @Test + public void testAddSameTypeTwiceInSameBundle_SilentlyDeduped() throws Exception { + final String symbolicName = "test.duplicate.type."+JavaClassNames.niceClassAndMethod(); + final String entityName = symbolicName+".type"; + + File jar = createJar(ImmutableMap.of("catalog.bom", Joiner.on("\n").join( + "brooklyn.catalog:", + " bundle: " + symbolicName, + " version: " + TEST_VERSION, + " items:", + " - id: " + entityName, + " itemType: entity", + " name: T", + " item:", + " type: org.apache.brooklyn.core.test.entity.TestEntity", + " - id: " + entityName, + " itemType: entity", + " name: T", + " item:", + " type: org.apache.brooklyn.core.test.entity.TestEntity"))); + + Response result = client().path("/catalog/bundles") + .header(HttpHeaders.CONTENT_TYPE, "application/x-jar") + .post(Streams.readFully(new FileInputStream(jar))); + HttpAsserts.assertHealthyStatusCode(result.getStatus()); + + TypeSummary entity = client().path("/catalog/types/" + entityName + "/" + TEST_VERSION) + .get(TypeSummary.class); + assertEquals(entity.getDisplayName(), "T"); + + List entities = client().path("/catalog/types/" + entityName) + .get(new GenericType>() {}); + Asserts.assertSize(entities, 1); + assertEquals(Iterables.getOnlyElement(entities), entity); + + BundleSummary bundle = client().path("/catalog/bundles/" + symbolicName + "/" + TEST_VERSION) + .get(BundleSummary.class); + Asserts.assertSize(bundle.getTypes(), 1); + assertEquals(Iterables.getOnlyElement(bundle.getTypes()), entity); + + } + + @Test + // different metadata is allowed as that doesn't affect operation (but different definition is not, see below) + // if in same bundle, it's deduped and last one wins; should warn and could disallow, but if type is pulled in + // multiple times from copied files, it feels convenient just to dedupe and forgive minor metadata changes; + // if in different bundles, see other test below, but in that case both are added + public void testAddSameTypeTwiceInSameBundleDifferentDisplayName_LastOneWins() throws Exception { + final String symbolicName = "test.duplicate.type."+JavaClassNames.niceClassAndMethod(); + final String entityName = symbolicName+".type"; + + File jar = createJar(ImmutableMap.of("catalog.bom", Joiner.on("\n").join( + "brooklyn.catalog:", + " bundle: " + symbolicName, + " version: " + TEST_VERSION, + " items:", + " - id: " + entityName, + " itemType: entity", + " name: T", + " item:", + " type: org.apache.brooklyn.core.test.entity.TestEntity", + " - id: " + entityName, + " itemType: entity", + " name: U", + " item:", + " type: org.apache.brooklyn.core.test.entity.TestEntity"))); + + Response result = client().path("/catalog/bundles") + .header(HttpHeaders.CONTENT_TYPE, "application/x-jar") + .post(Streams.readFully(new FileInputStream(jar))); + HttpAsserts.assertHealthyStatusCode(result.getStatus()); + + TypeSummary entity = client().path("/catalog/types/" + entityName + "/" + TEST_VERSION) + .get(TypeSummary.class); + assertEquals(entity.getDisplayName(), "U"); + + List entities = client().path("/catalog/types/" + entityName) + .get(new GenericType>() {}); + Asserts.assertSize(entities, 1); + assertEquals(Iterables.getOnlyElement(entities), entity); + + BundleSummary bundle = client().path("/catalog/bundles/" + symbolicName + "/" + TEST_VERSION) + .get(BundleSummary.class); + Asserts.assertSize(bundle.getTypes(), 1); + assertEquals(Iterables.getOnlyElement(bundle.getTypes()), entity); + } + + @Test + // would be confusing if the _definition_ is different however, as one will be ignored + public void testAddSameTypeTwiceInSameBundleDifferentDefinition_Disallowed() throws Exception { + final String symbolicName = "test.duplicate.type."+JavaClassNames.niceClassAndMethod(); + final String entityName = symbolicName+".type"; + final String entityNameOkay = symbolicName+".okayType"; + + File jar = createJar(ImmutableMap.of("catalog.bom", Joiner.on("\n").join( + "brooklyn.catalog:", + " bundle: " + symbolicName, + " version: " + TEST_VERSION, + " items:", + " - id: " + entityNameOkay, + " itemType: entity", + " item:", + " type: org.apache.brooklyn.core.test.entity.TestEntity", + " - id: " + entityName, + " itemType: entity", + " item:", + " type: org.apache.brooklyn.core.test.entity.TestEntity", + " a.config: first_definition", + " - id: " + entityName, + " itemType: entity", + " item:", + " type: org.apache.brooklyn.core.test.entity.TestEntity", + " a.config: second_definition_makes_it_different_so_disallowed"))); + + Response result = client().path("/catalog/bundles") + .header(HttpHeaders.CONTENT_TYPE, "application/x-jar") + .post(Streams.readFully(new FileInputStream(jar))); + HttpAsserts.assertNotHealthyStatusCode(result.getStatus()); + String resultS = Streams.readFullyString((InputStream)result.getEntity()); + Asserts.assertStringContainsIgnoreCase(resultS, "different plan", entityName); + Asserts.assertStringDoesNotContain(resultS, entityNameOkay); + + // entity not added + Response get = client().path("/catalog/types/" + entityName + "/" + TEST_VERSION).get(); + assertEquals(get.getStatus(), 404); + + List entities = client().path("/catalog/types/" + entityName) + .get(new GenericType>() {}); + Asserts.assertSize(entities, 0); + + // nor is the okay entity + Response getOkay = client().path("/catalog/types/" + entityNameOkay + "/" + TEST_VERSION).get(); + assertEquals(getOkay.getStatus(), 404); + + // and nor is the bundle + Response getBundle = client().path("/catalog/bundles/" + symbolicName + "/" + TEST_VERSION).get(); + assertEquals(getBundle.getStatus(), 404); + } + + // TODO might in future want to allow this if the user adding the type cannot see the other type due to entitlements; + // means however there might be another user who _can_ see the two different types + private final static boolean DISALLOW_DIFFERENCES_IN_SAME_TYPE_ID_FROM_DIFFERENT_BUNDLES = true; + + @Test + public void testAddSameTypeTwiceInDifferentBundleDifferentDefinition_Disallowed() throws Exception { + Preconditions.checkArgument(DISALLOW_DIFFERENCES_IN_SAME_TYPE_ID_FROM_DIFFERENT_BUNDLES); + // if above changed, assert that both types are added + + final String symbolicName1 = "test.duplicate.type."+JavaClassNames.niceClassAndMethod()+".1"; + final String symbolicName2 = "test.duplicate.type."+JavaClassNames.niceClassAndMethod()+".2"; + final String entityName = "test.duplicate.type."+JavaClassNames.niceClassAndMethod()+".type"; + + File jar1 = createJar(ImmutableMap.of("catalog.bom", Joiner.on("\n").join( + "brooklyn.catalog:", + " bundle: " + symbolicName1, + " version: " + TEST_VERSION, + " items:", + " - id: " + entityName, + " itemType: entity", + " item:", + " type: org.apache.brooklyn.core.test.entity.TestEntity", + " a.config: in_bundle1"))); + Response result1 = client().path("/catalog/bundles") + .header(HttpHeaders.CONTENT_TYPE, "application/x-jar") + .post(Streams.readFully(new FileInputStream(jar1))); + HttpAsserts.assertHealthyStatusCode(result1.getStatus()); + + File jar2 = createJar(ImmutableMap.of("catalog.bom", Joiner.on("\n").join( + "brooklyn.catalog:", + " bundle: " + symbolicName2, + " version: " + TEST_VERSION, + " items:", + " - id: " + entityName, + " itemType: entity", + " item:", + " type: org.apache.brooklyn.core.test.entity.TestEntity", + " a.config: in_bundle2"))); + Response result2 = client().path("/catalog/bundles") + .header(HttpHeaders.CONTENT_TYPE, "application/x-jar") + .post(Streams.readFully(new FileInputStream(jar2))); + String resultS = Streams.readFullyString((InputStream)result2.getEntity()); + HttpAsserts.assertNotHealthyStatusCode(result2.getStatus()); + Asserts.assertStringContainsIgnoreCase(resultS, "it is different to", entityName, "different bundle", symbolicName1); + } + + @Test + // TODO fails as there is NOT a link to a specific type in a specific bundle currently - API assumes type name+id unique + // but they may differ in the containing bundle (and maybe in future differ in more things) + // ==> bug about to be fixed! + public void testAddSameTypeTwiceInDifferentBundleSameDefinition_AllowedAndApiMakesTheDifferentOnesClear() throws Exception { + final String symbolicName1 = "test.duplicate.type."+JavaClassNames.niceClassAndMethod()+".1"; + final String symbolicName2 = "test.duplicate.type."+JavaClassNames.niceClassAndMethod()+".2"; + final String entityName = "test.duplicate.type."+JavaClassNames.niceClassAndMethod()+".type"; + + File jar1 = createJar(ImmutableMap.of("catalog.bom", Joiner.on("\n").join( + "brooklyn.catalog:", + " bundle: " + symbolicName1, + " version: " + TEST_VERSION, + " items:", + " - id: " + entityName, + " itemType: entity", + " name: T", + " item:", + " type: org.apache.brooklyn.core.test.entity.TestEntity"))); + Response result1 = client().path("/catalog/bundles") + .header(HttpHeaders.CONTENT_TYPE, "application/x-jar") + .post(Streams.readFully(new FileInputStream(jar1))); + HttpAsserts.assertHealthyStatusCode(result1.getStatus()); + + File jar2 = createJar(ImmutableMap.of("catalog.bom", Joiner.on("\n").join( + "brooklyn.catalog:", + " bundle: " + symbolicName2, + " version: " + TEST_VERSION, + " items:", + " - id: " + entityName, + " itemType: entity", + " name: U", + " item:", + " type: org.apache.brooklyn.core.test.entity.TestEntity"))); + Response result2 = client().path("/catalog/bundles") + .header(HttpHeaders.CONTENT_TYPE, "application/x-jar") + .post(Streams.readFully(new FileInputStream(jar2))); + HttpAsserts.assertHealthyStatusCode(result2.getStatus()); + + TypeSummary entity = client().path("/catalog/types/" + entityName + "/" + TEST_VERSION) + .get(TypeSummary.class); + // type should be present twice, but bundle 1 preferred because first alphanumerically; + assertEquals(entity.getContainingBundle(), symbolicName1+":"+TEST_VERSION); + // (however this might be weakened in future) +// Asserts.assertStringContains("["+symbolicName1+":"+TEST_VERSION+"] OR ["+symbolicName2+":"+TEST_VERSION+"]", +// "["+entity.getContainingBundle()+"]"); + TypeSummary entity1 = entity; + + List entities = client().path("/catalog/types/" + entityName) + .get(new GenericType>() {}); + Asserts.assertSize(entities, 2); + assertEquals(entities.get(0), entity1); + TypeSummary entity2 = entities.get(1); + assertEquals(entity2.getContainingBundle(), symbolicName2+":"+TEST_VERSION); + Assert.assertNotEquals(entity1, entity2); + + assertEquals(entity1.getDisplayName(), "T"); + assertEquals(entity2.getDisplayName(), "U"); + + BundleSummary bundle1 = client().path("/catalog/bundles/" + symbolicName1 + "/" + TEST_VERSION) + .get(BundleSummary.class); + Asserts.assertSize(bundle1.getTypes(), 1); + assertEquals(Iterables.getOnlyElement(bundle1.getTypes()), entity1); + + BundleSummary bundle2 = client().path("/catalog/bundles/" + symbolicName2 + "/" + TEST_VERSION) + .get(BundleSummary.class); + Asserts.assertSize(bundle2.getTypes(), 1); + assertEquals(Iterables.getOnlyElement(bundle2.getTypes()), entity2); + + @SuppressWarnings("unchecked") + String self1 = ((Map)entity1.getExtraFields().get("links")).get("self"); + @SuppressWarnings("unchecked") + String self2 = ((Map)entity2.getExtraFields().get("links")).get("self"); + + Assert.assertNotEquals(self1, self2); + + TypeSummary entity1r = client().path(self1).get(TypeSummary.class); + TypeSummary entity2r = client().path(self1).get(TypeSummary.class); + Assert.assertEquals(entity1r, entity1); + Assert.assertEquals(entity2r, entity2); + } + } From 93b2b99a0cd1806e511a79eb479c92118a6b71a4 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Fri, 5 Jan 2018 13:12:10 +0000 Subject: [PATCH 2/2] adds bundle-specific type endpoint and fixes test for self link distinguishing between same type id in different bundles --- .../apache/brooklyn/rest/api/BundleApi.java | 47 +++++++++++++++++++ .../brooklyn/rest/domain/TypeDetail.java | 16 +++++++ .../rest/resources/BundleResource.java | 43 +++++++++++++++++ .../rest/transform/TypeTransformer.java | 11 ++++- .../resources/BundleAndTypeResourcesTest.java | 24 +++++++--- 5 files changed, 133 insertions(+), 8 deletions(-) diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java index 2eb5ed8a96..2296fe3243 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java @@ -35,6 +35,8 @@ import org.apache.brooklyn.rest.domain.BundleInstallationRestResult; import org.apache.brooklyn.rest.domain.BundleSummary; +import org.apache.brooklyn.rest.domain.TypeDetail; +import org.apache.brooklyn.rest.domain.TypeSummary; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -88,6 +90,51 @@ public BundleSummary detail( @PathParam("version") String version); + + @Path("/{symbolicName}/{version}/types") + @GET + @ApiOperation(value = "Get all types declared in a given bundle", + response = TypeDetail.class) + public List getTypes( + @ApiParam(name = "symbolicName", value = "Bundle name to query", required = true) + @PathParam("symbolicName") + String symbolicName, + @ApiParam(name = "version", value = "Version of bundle and of type to query", required = true) + @PathParam("version") + String version); + + @Path("/{symbolicName}/{version}/types/{typeSymbolicName}") + @GET + @ApiOperation(value = "Get detail on a given type in a given bundle", + response = TypeDetail.class) + public TypeDetail getType( + @ApiParam(name = "symbolicName", value = "Bundle name to query", required = true) + @PathParam("symbolicName") + String symbolicName, + @ApiParam(name = "version", value = "Version of bundle and of type to query", required = true) + @PathParam("version") + String version, + @ApiParam(name = "typeSymbolicName", value = "Type name to query", required = true) + @PathParam("typeSymbolicName") + String typeSymbolicName); + + @Path("/{symbolicName}/{version}/types/{typeSymbolicName}/{typeVersion}") + @GET + @ApiOperation(value = "Get detail on a given type and version in a bundle (special method for unusual cases where type has different version)", + response = TypeDetail.class) + public TypeDetail getTypeExplicitVersion( + @ApiParam(name = "symbolicName", value = "Bundle name to query", required = true) + @PathParam("symbolicName") + String symbolicName, + @ApiParam(name = "version", value = "Bundle version to query", required = true) + @PathParam("version") + String version, + @ApiParam(name = "typeSymbolicName", value = "Type name to query", required = true) + @PathParam("typeSymbolicName") + String typeSymbolicName, + @ApiParam(name = "typeVersion", value = "Version to query (if different to bundle version)", required = true) + @PathParam("typeVersion") + String typeVersion); @Path("/{symbolicName}/{version}") @DELETE diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeDetail.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeDetail.java index 0fafcbeb03..4b84d606f2 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeDetail.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeDetail.java @@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.google.common.base.Objects; /** As {@link TypeSummary} but including plan information. */ public class TypeDetail extends TypeSummary { @@ -42,7 +43,22 @@ public String getFormat() { public Object getData() { return data; } + @Override + public int hashCode() { + return Objects.hashCode(format, data); + } + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + TypeImplementationPlanSummary other = (TypeImplementationPlanSummary) obj; + if (!Objects.equal(data, other.data)) return false; + if (!Objects.equal(format, other.format)) return false; + return true; + } } + private TypeImplementationPlanSummary plan; /** Constructor for JSON deserialization use only. */ diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java index 64cd6ed82a..433d8336b1 100644 --- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java @@ -27,14 +27,19 @@ import javax.ws.rs.core.Response.Status; import org.apache.brooklyn.api.typereg.ManagedBundle; +import org.apache.brooklyn.api.typereg.RegisteredType; import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog; import org.apache.brooklyn.core.mgmt.entitlement.Entitlements; import org.apache.brooklyn.core.mgmt.ha.OsgiBundleInstallationResult; import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; +import org.apache.brooklyn.core.typereg.RegisteredTypePredicates; +import org.apache.brooklyn.core.typereg.RegisteredTypes; import org.apache.brooklyn.rest.api.BundleApi; import org.apache.brooklyn.rest.domain.ApiError; import org.apache.brooklyn.rest.domain.BundleInstallationRestResult; import org.apache.brooklyn.rest.domain.BundleSummary; +import org.apache.brooklyn.rest.domain.TypeDetail; +import org.apache.brooklyn.rest.domain.TypeSummary; import org.apache.brooklyn.rest.filter.HaHotStateRequired; import org.apache.brooklyn.rest.transform.TypeTransformer; import org.apache.brooklyn.rest.util.WebResourceUtils; @@ -48,6 +53,7 @@ import com.google.common.base.Predicate; import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; @HaHotStateRequired public class BundleResource extends AbstractBrooklynRestResource implements BundleApi { @@ -107,6 +113,42 @@ protected ManagedBundle lookup(String symbolicName, String version) { } return b; } + + + @Override + public List getTypes(String symbolicName, String version) { + ManagedBundle b = lookup(symbolicName, version); + return TypeTransformer.bundleDetails(brooklyn(), b, ui.getBaseUriBuilder(), mgmt()).getTypes(); + } + + @Override + public TypeDetail getType(String symbolicName, String version, String typeSymbolicName) { + return getTypeExplicitVersion(symbolicName, version, typeSymbolicName, version); + } + + @Override + public TypeDetail getTypeExplicitVersion(String symbolicName, String version, String typeSymbolicName, String typeVersion) { + ManagedBundle b = lookup(symbolicName, version); + if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, typeSymbolicName+":"+typeVersion)) { + // TODO best to default to "not found" - unless maybe they have permission to "see null" + throw WebResourceUtils.forbidden("User '%s' not permitted to see info on this type (including whether or not installed)", + Entitlements.getEntitlementContext().user()); + } + + Predicate pred = RegisteredTypePredicates.nameOrAlias(typeSymbolicName); + pred = Predicates.and(pred, RegisteredTypePredicates.containingBundle(b.getVersionedName())); + if (!LATEST.equalsIgnoreCase(typeVersion)) { + pred = Predicates.and(pred, RegisteredTypePredicates.version(typeVersion)); + } + Iterable items = mgmt().getTypeRegistry().getMatching(pred); + + if (Iterables.isEmpty(items)) { + throw WebResourceUtils.notFound("Entity with id '%s:%s' not found", typeSymbolicName, typeVersion); + } + + RegisteredType item = RegisteredTypes.getBestVersion(items); + return TypeTransformer.detail(brooklyn(), item, ui.getBaseUriBuilder()); + } @Override public BundleInstallationRestResult remove(String symbolicName, String version, Boolean force) { @@ -170,4 +212,5 @@ public Response createFromArchive(byte[] zipInput, Boolean force) { } return Response.status(status).entity(resultR).build(); } + } diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java index 736ee83911..e8bfb5098b 100644 --- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java @@ -49,6 +49,7 @@ import org.apache.brooklyn.core.objs.BrooklynTypes; import org.apache.brooklyn.core.typereg.RegisteredTypePredicates; import org.apache.brooklyn.core.typereg.RegisteredTypes; +import org.apache.brooklyn.rest.api.BundleApi; import org.apache.brooklyn.rest.api.TypeApi; import org.apache.brooklyn.rest.domain.BundleInstallationRestResult; import org.apache.brooklyn.rest.domain.BundleSummary; @@ -61,6 +62,8 @@ import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils; import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.guava.Maybe; +import org.apache.brooklyn.util.osgi.VersionedName; import org.slf4j.LoggerFactory; import com.google.common.collect.Sets; @@ -185,7 +188,13 @@ protected static Map makeLinks(RegisteredType item, UriBuilder ub) } private static URI getSelfLink(RegisteredType item, UriBuilder ub) { - return serviceUriBuilder(ub, TypeApi.class, "detail").build(item.getSymbolicName(), item.getVersion()); + Maybe bundleM = VersionedName.parseMaybe(item.getContainingBundle(), true); + if (bundleM.isPresent()) { + return serviceUriBuilder(ub, BundleApi.class, "getTypeExplicitVersion").build(bundleM.get().getSymbolicName(), bundleM.get().getVersionString(), + item.getSymbolicName(), item.getVersion()); + } else { + return serviceUriBuilder(ub, TypeApi.class, "detail").build(item.getSymbolicName(), item.getVersion()); + } } private static String tidyIconLink(BrooklynRestResourceUtils b, RegisteredType item, String iconUrl, UriBuilder ub) { if (b.isUrlServerSideAndSafe(iconUrl)) { diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeResourcesTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeResourcesTest.java index 3672a7a0ce..a15b04a597 100644 --- a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeResourcesTest.java +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeResourcesTest.java @@ -698,12 +698,25 @@ public void testOsgiBundleWithBom() throws Exception { assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode()); - TypeSummary entityItem = client().path("/catalog/types/"+symbolicName + "/" + version) - .get(TypeSummary.class); - + TypeDetail entityItem = client().path("/catalog/types/"+symbolicName + "/" + version) + .get(TypeDetail.class); assertEquals(entityItem.getSymbolicName(), symbolicName); assertEquals(entityItem.getVersion(), version); + // assert we can cast it as summary + TypeSummary entityItemSummary = client().path("/catalog/types/"+symbolicName + "/" + version) + .get(TypeSummary.class); + assertEquals(entityItemSummary.getSymbolicName(), symbolicName); + assertEquals(entityItemSummary.getVersion(), version); + + List typesInBundle = client().path("/catalog/bundles/" + symbolicName + "/" + version + "/types") + .get(new GenericType>() {}); + assertEquals(Iterables.getOnlyElement(typesInBundle), entityItemSummary); + + TypeDetail entityItemFromBundle = client().path("/catalog/bundles/" + symbolicName + "/" + version + "/types/" + symbolicName + "/" + version) + .get(TypeDetail.class); + assertEquals(entityItemFromBundle, entityItem); + // and internally let's check we have libraries RegisteredType item = getManagementContext().getTypeRegistry().get(symbolicName, version); Assert.assertNotNull(item); @@ -1283,9 +1296,6 @@ public void testAddSameTypeTwiceInDifferentBundleDifferentDefinition_Disallowed( } @Test - // TODO fails as there is NOT a link to a specific type in a specific bundle currently - API assumes type name+id unique - // but they may differ in the containing bundle (and maybe in future differ in more things) - // ==> bug about to be fixed! public void testAddSameTypeTwiceInDifferentBundleSameDefinition_AllowedAndApiMakesTheDifferentOnesClear() throws Exception { final String symbolicName1 = "test.duplicate.type."+JavaClassNames.niceClassAndMethod()+".1"; final String symbolicName2 = "test.duplicate.type."+JavaClassNames.niceClassAndMethod()+".2"; @@ -1359,7 +1369,7 @@ public void testAddSameTypeTwiceInDifferentBundleSameDefinition_AllowedAndApiMak Assert.assertNotEquals(self1, self2); TypeSummary entity1r = client().path(self1).get(TypeSummary.class); - TypeSummary entity2r = client().path(self1).get(TypeSummary.class); + TypeSummary entity2r = client().path(self2).get(TypeSummary.class); Assert.assertEquals(entity1r, entity1); Assert.assertEquals(entity2r, entity2); }