Repository: EnMasseProject/enmasse (java)

Issue:
[api-model] schema obejct deserialization does not work
**Describe the bug**
schema obejct deserialization does not work

**To Reproduce**
Steps to reproduce the behavior:
1. get schema from api-server
2. try to deserialize json to schema obejct
3. See error

```
2019-03-18T09:13:15.639Z INFO  [AddressApiClient] GET-schema: path /apis/enmasse.io/v1beta1/namespaces/myproject/addressspaceschemas;
2019-03-18T09:13:16.741Z INFO  [AddressSpaceUtils] Got Schema object: {"apiVersion":"enmasse.io/v1beta1","kind":"AddressSpaceSchemaList","metadata":{},"items":[{"apiVersion":"enmasse.io/v1beta1","kind":"AddressSpaceSchema","metadata":{"annotations":{},"creationTimestamp":"2019-03-18T09:13:15.633Z","labels":{},"name":"brokered"},"spec":{"description":"A brokered address space consists of a broker combined with a console for managing addresses.","addressTypes":[{"name":"queue","description":"A queue that supports selectors, message grouping and transactions","plans":[{"name":"brokered-queue","description":"Creates a queue on a broker."}]},{"name":"topic","description":"A topic supports pub-sub semantics. Messages sent to a topic address is forwarded to all subscribes on that address.","plans":[{"name":"brokered-topic","description":"Creates a topic on a broker."}]}],"plans":[{"name":"brokered-single-broker","description":"Single Broker instance"}]}},{"apiVersion":"enmasse.io/v1beta1","kind":"AddressSpaceSchema","metadata":{"annotations":{},"creationTimestamp":"2019-03-18T09:13:15.633Z","labels":{},"name":"standard"},"spec":{"description":"A standard address space consists of an AMQP router network in combination with attachable 'storage units'. The implementation of a storage unit is hidden from the client and the routers with a well defined API.","addressTypes":[{"name":"anycast","description":"A direct messaging address type. Messages sent to an anycast address are not stored but forwarded directly to a consumer.","plans":[{"name":"standard-large-anycast","description":"Creates a large anycast address."},{"name":"standard-medium-anycast","description":"Creates a medium anycast address."},{"name":"standard-small-anycast","description":"Creates a small anycast address."}]},{"name":"multicast","description":"A direct messaging address type. Messages sent to a multicast address are not stored but forwarded directly to multiple consumers.","plans":[{"name":"standard-small-multicast","description":"Creates a small multicast address."},{"name":"standard-large-multicast","description":"Creates a large multicast address."},{"name":"standard-medium-multicast","description":"Creates a medium multicast address."}]},{"name":"queue","description":"A store-and-forward queue. A queue may be sharded across multiple storage units, in which case message ordering is no longer guaranteed.","plans":[{"name":"standard-large-queue","description":"Creates a large queue backed by a dedicated broker."},{"name":"standard-xlarge-queue","description":"Creates an extra large queue backed by 2 brokers."},{"name":"standard-small-queue","description":"Creates a small queue sharing underlying broker with other queues."},{"name":"standard-medium-queue","description":"Creates a medium sized queue sharing underlying broker with other queues."},{"name":"test-schema-rest-api-addr-plan","description":"Custom systemtests defined address plan"}]},{"name":"topic","description":"A topic address for store-and-forward publish-subscribe messaging. Each message published to a topic address is forwarded to all subscribes on that address.","plans":[{"name":"standard-medium-topic","description":"Creates a medium sized topic sharing underlying broker with other topics."},{"name":"standard-large-topic","description":"Creates a large topic backed by a dedicated broker."},{"name":"standard-xlarge-topic","description":"Creates an extra large topic backed by 2 brokers."},{"name":"standard-small-topic","description":"Creates a small topic sharing underlying broker with other topics."}]},{"name":"subscription","description":"A subscription on a topic","plans":[{"name":"standard-small-subscription","description":"Creates a small durable subscription on a topic."},{"name":"standard-large-subscription","description":"Creates a large durable subscription on a topic."},{"name":"standard-medium-subscription","description":"Creates a medium durable subscription on a topic."}]}],"plans":[{"name":"standard-small","description":"Messaging infrastructure based on Apache Qpid Dispatch Router and Apache ActiveMQ Artemis"},{"name":"schema-rest-api-plan","description":"Custom systemtests defined address space plan"},{"name":"standard-unlimited","description":"Messaging infrastructure based on Apache Qpid Dispatch Router and Apache ActiveMQ Artemis."},{"name":"standard-unlimited-with-mqtt","description":"Messaging infrastructure based on Apache Qpid Dispatch Router and Apache ActiveMQ Artemis and MQTT support."},{"name":"standard-medium","description":"Messaging infrastructure based on Apache Qpid Dispatch Router and Apache ActiveMQ Artemis."}]}}]}
2019-03-18T09:13:16.808Z INFO  [ApiClient] DELETE-address-space-plan: path '/apis/admin.enmasse.io/v1beta2/namespaces/myproject/addressspaceplans/schema-rest-api-plan'
2019-03-18T09:13:16.840Z INFO  [ApiClient] DELETE-address-plan: path '/apis/admin.enmasse.io/v1beta2/namespaces/myproject/addressplans/test-schema-rest-api-addr-plan'
2019-03-18T09:13:16.857Z INFO  [TestBase] All addressspaces will be removed
2019-03-18T09:13:16.858Z INFO  [TimeMeasuringSystem] End time of operation TEST_EXECUTION is correctly stored
2019-03-18T09:13:16.864Z ERROR [ITestSeparator] Caught exception
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "apiVersion" (class io.enmasse.address.model.Schema), not marked as ignorable (3 known properties: "authenticationServices", "addressSpaceTypes", "creationTimestamp"])
 at [Source: (String)"{"apiVersion":"enmasse.io/v1beta1","kind":"AddressSpaceSchemaList","metadata":{},"items":[{"apiVersion":"enmasse.io/v1beta1","kind":"AddressSpaceSchema","metadata":{"annotations":{},"creationTimestamp":"2019-03-18T09:13:15.633Z","labels":{},"name":"brokered"},"spec":{"description":"A brokered address space consists of a broker combined with a console for managing addresses.","addressTypes":[{"name":"queue","description":"A queue that supports selectors, message grouping and transactions","plans""[truncated 4090 chars]; line: 1, column: 16] (through reference chain: io.enmasse.address.model.Schema["apiVersion"])
	at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61) ~[jackson-databind-2.9.8.jar:2.9.8]
	at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:823) ~[jackson-databind-2.9.8.jar:2.9.8]
	at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1153) ~[jackson-databind-2.9.8.jar:2.9.8]
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1589) ~[jackson-databind-2.9.8.jar:2.9.8]
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1567) ~[jackson-databind-2.9.8.jar:2.9.8]
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:294) ~[jackson-databind-2.9.8.jar:2.9.8]
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151) ~[jackson-databind-2.9.8.jar:2.9.8]
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013) ~[jackson-databind-2.9.8.jar:2.9.8]
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3004) ~[jackson-databind-2.9.8.jar:2.9.8]
	at io.enmasse.systemtest.utils.AddressSpaceUtils.getSchema(AddressSpaceUtils.java:350) ~[classes/:na]
	at io.enmasse.systemtest.bases.TestBase.getSchema(TestBase.java:486) ~[test-classes/:na]
	at io.enmasse.systemtest.common.api.ApiServerTest.testRestApiGetSchema(ApiServerTest.java:86) ~[test-classes/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:628) ~[junit-platform-commons-1.4.0.jar:1.4.0]
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:117) ~[junit-jupiter-engine-5.4.0.jar:5.4.0]
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:184) ~[junit-jupiter-engine-5.4.0.jar:5.4.0]
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:180) ~[junit-jupiter-engine-5.4.0.jar:5.4.0]
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:127) ~[junit-jupiter-engine-5.4.0.jar:5.4.0]
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) ~[junit-jupiter-engine-5.4.0.jar:5.4.0]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1540) ~[na:na]
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1540) ~[na:na]
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51) ~[junit-platform-engine-1.4.0.jar:1.4.0]
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229) ~[junit-platform-launcher-1.4.0.jar:1.4.0]
	at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197) ~[junit-platform-launcher-1.4.0.jar:1.4.0]
	at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211) ~[junit-platform-launcher-1.4.0.jar:1.4.0]
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191) ~[junit-platform-launcher-1.4.0.jar:1.4.0]
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128) ~[junit-platform-launcher-1.4.0.jar:1.4.0]
	at org.junit.platform.surefire.provider.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:155) ~[junit-platform-surefire-provider-1.3.2.jar:1.3.2]
	at org.junit.platform.surefire.provider.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:134) ~[junit-platform-surefire-provider-1.3.2.jar:1.3.2]
	at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:384) ~[surefire-booter-2.22.1.jar:2.22.1]
	at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:345) ~[surefire-booter-2.22.1.jar:2.22.1]
	at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:126) ~[surefire-booter-2.22.1.jar:2.22.1]
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:418) ~[surefire-booter-2.22.1.jar:2.22.1]
```


Pull request description:
Add serialization test of address space schema
* Add resource limits and resource usage by plans to output
* Add available authentication services to output
* Remove undocumented schema endpoint
* Rewrite systemtest to check official schema endpoint

Fixes #2461

Patch:
diff --git a/api-model/src/main/java/io/enmasse/address/model/NamedDescription.java b/api-model/src/main/java/io/enmasse/address/model/AddressPlanDescription.java
similarity index 54%
rename from api-model/src/main/java/io/enmasse/address/model/NamedDescription.java
rename to api-model/src/main/java/io/enmasse/address/model/AddressPlanDescription.java
index 56beb29f8..7e0486024 100644
--- a/api-model/src/main/java/io/enmasse/address/model/NamedDescription.java
+++ b/api-model/src/main/java/io/enmasse/address/model/AddressPlanDescription.java
@@ -8,12 +8,17 @@ import javax.validation.constraints.NotNull;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 
-import io.enmasse.common.model.AbstractHasMetadata;
+import io.enmasse.admin.model.v1.AbstractWithAdditionalProperties;
 import io.fabric8.kubernetes.api.model.Doneable;
 import io.sundr.builder.annotations.Buildable;
 import io.sundr.builder.annotations.BuildableReference;
 import io.sundr.builder.annotations.Inline;
 
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
 /**
  * A class containing a name and optional description.
  */
@@ -21,7 +26,7 @@ import io.sundr.builder.annotations.Inline;
         editableEnabled = false,
         generateBuilderPackage = false,
         builderPackage = "io.fabric8.kubernetes.api.builder",
-        refs= {@BuildableReference(AbstractHasMetadata.class)},
+        refs= {@BuildableReference(AbstractWithAdditionalProperties.class)},
         inline = @Inline(
                 type = Doneable.class,
                 prefix = "Doneable",
@@ -29,18 +34,20 @@ import io.sundr.builder.annotations.Inline;
                 )
         )
 @JsonInclude(JsonInclude.Include.NON_NULL)
-public class NamedDescription {
+public class AddressPlanDescription extends AbstractWithAdditionalProperties {
 
     @NotNull
     private String name;
     private String description;
+    private Map<String, Double> resources = new HashMap<>();
 
-    public NamedDescription() {
+    public AddressPlanDescription() {
     }
 
-    public NamedDescription(final String name, final String description) {
+    public AddressPlanDescription(final String name, final String description, Map<String, Double> resources) {
         this.name = name;
         this.description = description;
+        this.resources = new HashMap<>(resources);
     }
 
     public String getName() {
@@ -59,13 +66,36 @@ public class NamedDescription {
         this.description = description;
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        AddressPlanDescription that = (AddressPlanDescription) o;
+        return Objects.equals(name, that.name) &&
+                Objects.equals(description, that.description) &&
+                Objects.equals(resources, that.resources);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, description, resources);
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder("{");
         sb.append("name=").append(this.name);
         sb.append(",");
         sb.append("description=").append(this.description);
+        sb.append("resources=").append(this.resources);
         return sb.append("}").toString();
     }
 
+    public Map<String, Double> getResources() {
+        return Collections.unmodifiableMap(resources);
+    }
+
+    public void setResources(Map<String, Double> resources) {
+        this.resources = new HashMap<>(resources);
+    }
 }
diff --git a/api-model/src/main/java/io/enmasse/address/model/AddressSpacePlanDescription.java b/api-model/src/main/java/io/enmasse/address/model/AddressSpacePlanDescription.java
new file mode 100644
index 000000000..f9769f909
--- /dev/null
+++ b/api-model/src/main/java/io/enmasse/address/model/AddressSpacePlanDescription.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2017-2018, EnMasse authors.
+ * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
+ */
+package io.enmasse.address.model;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.enmasse.admin.model.v1.AbstractWithAdditionalProperties;
+import io.fabric8.kubernetes.api.model.Doneable;
+import io.sundr.builder.annotations.Buildable;
+import io.sundr.builder.annotations.BuildableReference;
+import io.sundr.builder.annotations.Inline;
+
+import javax.validation.constraints.NotNull;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * A class containing a name and optional description.
+ */
+@Buildable(
+        editableEnabled = false,
+        generateBuilderPackage = false,
+        builderPackage = "io.fabric8.kubernetes.api.builder",
+        refs= {@BuildableReference(AbstractWithAdditionalProperties.class)},
+        inline = @Inline(
+                type = Doneable.class,
+                prefix = "Doneable",
+                value = "done"
+                )
+        )
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class AddressSpacePlanDescription extends AbstractWithAdditionalProperties {
+
+    @NotNull
+    private String name;
+    private String description;
+    private Map<String, Double> resourceLimits = new HashMap<>();
+
+    public AddressSpacePlanDescription() {
+    }
+
+    public AddressSpacePlanDescription(final String name, final String description, Map<String, Double> resourceLimits) {
+        this.name = name;
+        this.description = description;
+        this.resourceLimits = resourceLimits;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        AddressSpacePlanDescription that = (AddressSpacePlanDescription) o;
+        return Objects.equals(name, that.name) &&
+                Objects.equals(description, that.description) &&
+                Objects.equals(resourceLimits, that.resourceLimits);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, description, resourceLimits);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("{");
+        sb.append("name=").append(this.name);
+        sb.append(",");
+        sb.append("description=").append(this.description);
+        sb.append("resourceLimits=").append(this.resourceLimits);
+        return sb.append("}").toString();
+    }
+
+    public Map<String, Double> getResourceLimits() {
+        return Collections.unmodifiableMap(resourceLimits);
+    }
+
+    public void setResourceLimits(Map<String, Double> resourceLimits) {
+        this.resourceLimits = new HashMap<>(resourceLimits);
+    }
+}
diff --git a/api-model/src/main/java/io/enmasse/address/model/AddressSpaceSchema.java b/api-model/src/main/java/io/enmasse/address/model/AddressSpaceSchema.java
index 315e058d7..c986c77e4 100644
--- a/api-model/src/main/java/io/enmasse/address/model/AddressSpaceSchema.java
+++ b/api-model/src/main/java/io/enmasse/address/model/AddressSpaceSchema.java
@@ -7,6 +7,7 @@ package io.enmasse.address.model;
 import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
 
+import io.enmasse.admin.model.v1.AuthenticationService;
 import io.enmasse.common.model.AbstractHasMetadata;
 import io.enmasse.common.model.DefaultCustomResource;
 import io.fabric8.kubernetes.api.model.Doneable;
@@ -14,6 +15,10 @@ import io.sundr.builder.annotations.Buildable;
 import io.sundr.builder.annotations.BuildableReference;
 import io.sundr.builder.annotations.Inline;
 
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
 @Buildable(
         editableEnabled = false,
         generateBuilderPackage = false,
@@ -46,7 +51,7 @@ public class AddressSpaceSchema extends AbstractHasMetadata<AddressSpaceSchema>
         return this.spec;
     }
 
-    public static AddressSpaceSchema fromAddressSpaceType(final String creationTimestamp, final AddressSpaceType addressSpaceType) {
+    public static AddressSpaceSchema fromAddressSpaceType(final String creationTimestamp, final AddressSpaceType addressSpaceType, final List<AuthenticationService> authenticationServiceList) {
         if (addressSpaceType == null) {
             return null;
         }
@@ -57,11 +62,35 @@ public class AddressSpaceSchema extends AbstractHasMetadata<AddressSpaceSchema>
                 .withCreationTimestamp(creationTimestamp)
                 .endMetadata()
 
-                .withSpec(AddressSpaceSchemaSpec.fromAddressSpaceType(addressSpaceType))
-
+                .editOrNewSpec()
+                .withDescription(addressSpaceType.getDescription())
+                .withAddressTypes(addressSpaceType.getAddressTypes().stream()
+                        .map(AddressTypeInformation::fromAddressType)
+                        .collect(Collectors.toList()))
+                .withPlans(addressSpaceType.getPlans().stream()
+                        .map(plan -> new AddressSpacePlanDescription(plan.getMetadata().getName(), plan.getShortDescription(), plan.getResourceLimits()))
+                        .collect(Collectors.toList()))
+                .withAuthenticationServices(authenticationServiceList.stream()
+                        .map(authenticationService -> authenticationService.getMetadata().getName())
+                        .collect(Collectors.toList()))
+                .endSpec()
                 .build();
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        AddressSpaceSchema that = (AddressSpaceSchema) o;
+        return Objects.equals(spec, that.spec) &&
+                Objects.equals(getMetadata(), that.getMetadata());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(spec, getMetadata());
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder("{");
diff --git a/api-model/src/main/java/io/enmasse/address/model/AddressSpaceSchemaList.java b/api-model/src/main/java/io/enmasse/address/model/AddressSpaceSchemaList.java
index 03cb18a97..3eb0615cb 100644
--- a/api-model/src/main/java/io/enmasse/address/model/AddressSpaceSchemaList.java
+++ b/api-model/src/main/java/io/enmasse/address/model/AddressSpaceSchemaList.java
@@ -27,7 +27,7 @@ public class AddressSpaceSchemaList extends AbstractList<AddressSpaceSchema> {
 
         final AddressSpaceSchemaList list = new AddressSpaceSchemaList();
         final List<AddressSpaceSchema> items = schema.getAddressSpaceTypes().stream()
-                .map(type -> AddressSpaceSchema.fromAddressSpaceType(schema.getCreationTimestamp(), type))
+                .map(type -> AddressSpaceSchema.fromAddressSpaceType(schema.getCreationTimestamp(), type, schema.getAuthenticationServices()))
                 .collect(Collectors.toList());
 
         list.setItems(items);
diff --git a/api-model/src/main/java/io/enmasse/address/model/AddressSpaceSchemaSpec.java b/api-model/src/main/java/io/enmasse/address/model/AddressSpaceSchemaSpec.java
index 7fa5937b1..8e12f3d95 100644
--- a/api-model/src/main/java/io/enmasse/address/model/AddressSpaceSchemaSpec.java
+++ b/api-model/src/main/java/io/enmasse/address/model/AddressSpaceSchemaSpec.java
@@ -6,6 +6,7 @@ package io.enmasse.address.model;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.stream.Collectors;
 
 import javax.validation.Valid;
@@ -15,6 +16,7 @@ import io.fabric8.kubernetes.api.model.Doneable;
 import io.sundr.builder.annotations.Buildable;
 import io.sundr.builder.annotations.BuildableReference;
 import io.sundr.builder.annotations.Inline;
+import io.sundr.shaded.org.apache.velocity.util.ArrayListWrapper;
 
 @Buildable(
         editableEnabled = false,
@@ -28,7 +30,8 @@ import io.sundr.builder.annotations.Inline;
 public class AddressSpaceSchemaSpec {
     private String description;
     private List<@Valid AddressTypeInformation> addressTypes = new ArrayList<>();
-    private List<@Valid NamedDescription> plans = new ArrayList<>();
+    private List<@Valid AddressSpacePlanDescription> plans = new ArrayList<>();
+    private List<@Valid String> authenticationServices = new ArrayList<>();
 
     public String getDescription() {
         return this.description;
@@ -46,29 +49,36 @@ public class AddressSpaceSchemaSpec {
         this.addressTypes = addressTypes;
     }
 
-    public List<NamedDescription> getPlans() {
+    public List<AddressSpacePlanDescription> getPlans() {
         return this.plans;
     }
 
-    public void setPlans(final List<NamedDescription> plans) {
+    public void setPlans(final List<AddressSpacePlanDescription> plans) {
         this.plans = plans;
     }
 
-    public static AddressSpaceSchemaSpec fromAddressSpaceType(final AddressSpaceType type ) {
-        if ( type == null ) {
-            return null;
-        }
+    public List<String> getAuthenticationServices() {
+        return authenticationServices;
+    }
+
+    public void setAuthenticationServices(List<String> authenticationServices) {
+        this.authenticationServices = authenticationServices;
+    }
 
-        return new AddressSpaceSchemaSpecBuilder()
-                .withDescription(type.getDescription())
-                .withAddressTypes(type.getAddressTypes().stream()
-                        .map(addressType -> AddressTypeInformation.fromAddressType(addressType))
-                        .collect(Collectors.toList())
-                        )
-                .withPlans(type.getPlans().stream()
-                        .map(plan -> new NamedDescription(plan.getMetadata().getName(), plan.getShortDescription()))
-                        .collect(Collectors.toList()))
-                .build();
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        AddressSpaceSchemaSpec that = (AddressSpaceSchemaSpec) o;
+        return Objects.equals(description, that.description) &&
+                Objects.equals(addressTypes, that.addressTypes) &&
+                Objects.equals(plans, that.plans) &&
+                Objects.equals(authenticationServices, that.authenticationServices);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(description, addressTypes, plans, authenticationServices);
     }
 
     @Override
@@ -79,6 +89,7 @@ public class AddressSpaceSchemaSpec {
         sb.append("plans=").append(this.plans);
         sb.append(",");
         sb.append("addressTypes=").append(this.addressTypes);
+        sb.append("authenticationServices=").append(this.authenticationServices);
         return sb.append("}").toString();
     }
 }
diff --git a/api-model/src/main/java/io/enmasse/address/model/AddressTypeInformation.java b/api-model/src/main/java/io/enmasse/address/model/AddressTypeInformation.java
index 69a261cd6..9c0c94b94 100644
--- a/api-model/src/main/java/io/enmasse/address/model/AddressTypeInformation.java
+++ b/api-model/src/main/java/io/enmasse/address/model/AddressTypeInformation.java
@@ -6,6 +6,7 @@ package io.enmasse.address.model;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.stream.Collectors;
 
 import javax.validation.Valid;
@@ -35,7 +36,7 @@ public class AddressTypeInformation {
     private String name;
     private String description;
 
-    private List<@Valid NamedDescription> plans = new ArrayList<>();
+    private List<@Valid AddressPlanDescription> plans = new ArrayList<>();
 
     public String getName() {
         return name;
@@ -53,14 +54,29 @@ public class AddressTypeInformation {
         this.description = description;
     }
 
-    public void setPlans(final List<NamedDescription> plans) {
+    public void setPlans(final List<AddressPlanDescription> plans) {
         this.plans = plans;
     }
 
-    public List<NamedDescription> getPlans() {
+    public List<AddressPlanDescription> getPlans() {
         return plans;
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        AddressTypeInformation that = (AddressTypeInformation) o;
+        return Objects.equals(name, that.name) &&
+                Objects.equals(description, that.description) &&
+                Objects.equals(plans, that.plans);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, description, plans);
+    }
+
     public static AddressTypeInformation fromAddressType(final AddressType addressType) {
         if (addressType == null) {
             return null;
@@ -70,7 +86,7 @@ public class AddressTypeInformation {
                 .withName(addressType.getName())
                 .withDescription(addressType.getDescription())
                 .withPlans(addressType.getPlans().stream()
-                        .map(plan -> new NamedDescription(plan.getMetadata().getName(), plan.getShortDescription()))
+                        .map(plan -> new AddressPlanDescription(plan.getMetadata().getName(), plan.getShortDescription(), plan.getResources()))
                         .collect(Collectors.toList()))
                 .build();
     }
diff --git a/api-server/src/main/java/io/enmasse/api/v1/http/HttpSchemaService.java b/api-server/src/main/java/io/enmasse/api/v1/http/HttpSchemaService.java
index 743f29e2b..0b021b206 100644
--- a/api-server/src/main/java/io/enmasse/api/v1/http/HttpSchemaService.java
+++ b/api-server/src/main/java/io/enmasse/api/v1/http/HttpSchemaService.java
@@ -53,7 +53,7 @@ public class HttpSchemaService {
             if (type == null) {
                 return Response.status(404).entity(Status.notFound("AddressSpaceSchema", addressSpaceType)).build();
             } else {
-                return Response.ok(fromAddressSpaceType(schema.getCreationTimestamp(), type)).build();
+                return Response.ok(fromAddressSpaceType(schema.getCreationTimestamp(), type, schema.getAuthenticationServices())).build();
             }
         } catch (Exception e) {
             log.warn("Exception handling GET schema", e);


Test patch:
diff --git a/api-model/src/test/java/io/enmasse/address/model/v1/address/SerializationTest.java b/api-model/src/test/java/io/enmasse/address/model/v1/address/SerializationTest.java
index 5ba7cfaa9..24dfee76a 100644
--- a/api-model/src/test/java/io/enmasse/address/model/v1/address/SerializationTest.java
+++ b/api-model/src/test/java/io/enmasse/address/model/v1/address/SerializationTest.java
@@ -22,22 +22,16 @@ import java.util.*;
 import javax.validation.ConstraintViolationException;
 import javax.validation.ValidationException;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
+import io.enmasse.address.model.*;
+import io.enmasse.address.model.AuthenticationServiceType;
 import io.enmasse.admin.model.v1.*;
+import io.enmasse.admin.model.v1.AuthenticationService;
+import io.enmasse.admin.model.v1.AuthenticationServiceBuilder;
 import org.junit.jupiter.api.Test;
 import org.mockito.internal.util.collections.Sets;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 
-import io.enmasse.address.model.Address;
-import io.enmasse.address.model.AddressBuilder;
-import io.enmasse.address.model.AddressList;
-import io.enmasse.address.model.AddressSpace;
-import io.enmasse.address.model.AddressSpaceBuilder;
-import io.enmasse.address.model.AddressSpaceList;
-import io.enmasse.address.model.AddressSpaceSchema;
-import io.enmasse.address.model.AuthenticationServiceType;
-import io.enmasse.address.model.Phase;
 import io.fabric8.kubernetes.api.model.networking.NetworkPolicyIngressRuleBuilder;
 
 // TODO: Add more tests of invalid input to deserialization
@@ -1035,4 +1029,76 @@ public class SerializationTest {
         assertThat(value.getStatus().getPhase(), is(Phase.Configuring));
 
     }
+
+    @Test
+    public void testSerializeAddressSpaceSchema() throws IOException {
+        AuthenticationService authService = new AuthenticationServiceBuilder()
+                        .editOrNewMetadata()
+                        .withName("auth1")
+                        .endMetadata()
+                        .withNewSpec()
+                        .withType(io.enmasse.admin.model.v1.AuthenticationServiceType.none)
+                        .endSpec()
+                        .build();
+
+        AddressPlan addressPlan = new AddressPlanBuilder()
+                .editOrNewMetadata()
+                .withName("small-queue")
+                .endMetadata()
+                .editOrNewSpec()
+                .withAddressType("queue")
+                .withShortDescription("my plan")
+                .addToResources("broker", 0.5)
+                .endSpec()
+                .build();
+
+        AddressType addressType = new AddressType();
+        addressType.setName("queue");
+        addressType.setDescription("my queue");
+        addressType.setPlans(Collections.singletonList(addressPlan));
+
+
+        AddressSpacePlan addressSpacePlan = new AddressSpacePlanBuilder()
+                .editOrNewMetadata()
+                .withName("small")
+                .endMetadata()
+                .editOrNewSpec()
+                .withInfraConfigRef("infra")
+                .addToAddressPlans("small-queue")
+                .withAddressSpaceType("type1")
+                .addToResourceLimits("broker", 1.0)
+                .endSpec()
+                .build();
+
+        EndpointSpec endpointSpec = new EndpointSpecBuilder()
+                .withName("messaging")
+                .withService("messaging")
+                .build();
+
+        BrokeredInfraConfig infraConfig = new BrokeredInfraConfigBuilder()
+                .editOrNewMetadata()
+                .withName("infra")
+                .endMetadata()
+                .editOrNewSpec()
+                .withVersion("1.0")
+                .endSpec()
+                .build();
+
+        AddressSpaceType addressSpaceType = new AddressSpaceType(
+                "type1",
+                "awesometype",
+                Collections.singletonList(addressSpacePlan),
+                Collections.singletonList(addressType),
+                Collections.singletonList(endpointSpec),
+                Collections.singletonList(infraConfig));
+
+        AddressSpaceSchema schema = AddressSpaceSchema.fromAddressSpaceType("0", addressSpaceType, Collections.singletonList(authService));
+
+        ObjectMapper mapper = new ObjectMapper();
+        String serialized = mapper.writeValueAsString(schema);
+        System.out.println(serialized);
+
+        AddressSpaceSchema deserialized = mapper.readValue(serialized, AddressSpaceSchema.class);
+        assertEquals(deserialized, schema);
+    }
 }
diff --git a/systemtests/src/main/java/io/enmasse/systemtest/utils/AddressSpaceUtils.java b/systemtests/src/main/java/io/enmasse/systemtest/utils/AddressSpaceUtils.java
index 545625f5c..d770bd174 100644
--- a/systemtests/src/main/java/io/enmasse/systemtest/utils/AddressSpaceUtils.java
+++ b/systemtests/src/main/java/io/enmasse/systemtest/utils/AddressSpaceUtils.java
@@ -343,11 +343,11 @@ public class AddressSpaceUtils {
         return endpoints.stream().filter(endpointStatus -> endpointStatus.getServiceHost().startsWith(serviceName)).findAny().get();
     }
 
-    public static Future<Schema> getSchema(AddressApiClient apiClient) throws Exception {
+    public static Future<AddressSpaceSchemaList> getSchema(AddressApiClient apiClient) throws Exception {
         JsonObject response = apiClient.getSchema();
-        CompletableFuture<Schema> schema = new CompletableFuture<>();
+        CompletableFuture<AddressSpaceSchemaList> schema = new CompletableFuture<>();
         log.info("Got Schema object: {}", response.toString());
-        schema.complete(new ObjectMapper().readValue(response.toString(), Schema.class));
+        schema.complete(new ObjectMapper().readValue(response.toString(), AddressSpaceSchemaList.class));
         return schema;
     }
 }
diff --git a/systemtests/src/test/java/io/enmasse/systemtest/bases/TestBase.java b/systemtests/src/test/java/io/enmasse/systemtest/bases/TestBase.java
index 5198b206e..d14f9b438 100644
--- a/systemtests/src/test/java/io/enmasse/systemtest/bases/TestBase.java
+++ b/systemtests/src/test/java/io/enmasse/systemtest/bases/TestBase.java
@@ -8,6 +8,7 @@ package io.enmasse.systemtest.bases;
 import com.google.common.collect.Ordering;
 import io.enmasse.address.model.Address;
 import io.enmasse.address.model.AddressSpace;
+import io.enmasse.address.model.AddressSpaceSchemaList;
 import io.enmasse.address.model.Schema;
 import io.enmasse.systemtest.*;
 import io.enmasse.systemtest.ability.ITestBase;
@@ -482,7 +483,7 @@ public abstract class TestBase implements ITestBase, ITestSeparator {
      * @return schema object
      * @throws Exception
      */
-    protected Future<Schema> getSchema() throws Exception {
+    protected Future<AddressSpaceSchemaList> getSchema() throws Exception {
         return AddressSpaceUtils.getSchema(addressApiClient);
     }
 
diff --git a/systemtests/src/test/java/io/enmasse/systemtest/common/api/ApiServerTest.java b/systemtests/src/test/java/io/enmasse/systemtest/common/api/ApiServerTest.java
index b8a1a211d..e1a49ed7d 100644
--- a/systemtests/src/test/java/io/enmasse/systemtest/common/api/ApiServerTest.java
+++ b/systemtests/src/test/java/io/enmasse/systemtest/common/api/ApiServerTest.java
@@ -83,31 +83,46 @@ class ApiServerTest extends TestBase {
                 "default", AddressSpaceType.STANDARD, resources, addressPlans);
         plansProvider.createAddressSpacePlan(addressSpacePlan);
 
-        Future<Schema> data = getSchema();
-        Schema schemaData = data.get(20, TimeUnit.SECONDS);
+        Future<AddressSpaceSchemaList> data = getSchema();
+        AddressSpaceSchemaList schemaData = data.get(20, TimeUnit.SECONDS);
         log.info("Check if schema object is not null");
-        assertThat(schemaData.getAddressSpaceTypes().size(), not(0));
+        assertThat(schemaData.getItems().size(), not(0));
 
         log.info("Check if the 'standard' address space type is found");
-        assertThat(schemaData.findAddressSpaceType("standard"), notNullValue());
+        AddressSpaceSchema standardSchema = findTypeWithName(schemaData, "standard");
+        assertNotNull(standardSchema);
 
         log.info("Check if the 'standard' address space has plans");
-        assertThat(schemaData.findAddressSpaceType("standard").get().getPlans(), notNullValue());
+        assertThat(standardSchema.getSpec().getPlans(), notNullValue());
 
         log.info("Check if schema object contains new address space plan");
-        assertTrue(schemaData.findAddressSpaceType("standard").get().getPlans()
+        assertTrue(standardSchema.getSpec().getPlans()
                 .stream()
-                .map(plan -> plan.getMetadata().getName())
+                .map(AddressSpacePlanDescription::getName)
                 .collect(Collectors.toList()).contains("schema-rest-api-plan"));
 
+        AddressTypeInformation addressType = standardSchema.getSpec().getAddressTypes().stream()
+                .filter(type -> "queue".equals(type.getName()))
+                .findFirst().orElse(null);
+        assertNotNull(addressType);
+
         log.info("Check if schema contains new address plans");
-        assertTrue(schemaData.findAddressSpaceType("standard").get().findAddressType("queue").get().getPlans().stream()
-                .filter(s -> s.getMetadata().getName().equals("test-schema-rest-api-addr-plan"))
-                .map(plan -> plan.getMetadata().getName())
+        assertTrue(addressType.getPlans().stream()
+                .filter(s -> s.getName().equals("test-schema-rest-api-addr-plan"))
+                .map(AddressPlanDescription::getName)
                 .collect(Collectors.toList())
                 .contains("test-schema-rest-api-addr-plan"));
     }
 
+    private AddressSpaceSchema findTypeWithName(AddressSpaceSchemaList schemaData, String name) {
+        for (AddressSpaceSchema schema : schemaData.getItems()) {
+            if (schema.getMetadata().getName().equals(name)) {
+                return schema;
+            }
+        }
+        return null;
+    }
+
     @Test
     void testConsoleMessagingMqttRoutes() throws Exception {
         AddressSpace addressSpace = AddressSpaceUtils.createAddressSpaceObject("routes-space", AddressSpaceType.STANDARD, AuthenticationServiceType.STANDARD);