From 32271a6f82042a2d83c7292002cd09f6f56b4e50 Mon Sep 17 00:00:00 2001
From: Jean-Noël Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Thu, 22 Sep 2016 08:43:15 +0000
Subject: [PATCH] OPENDJ-3246 Support polymorphism in CREST API and OpenAPI descriptors
---
opendj-bom/pom.xml | 2
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ResourceTypePropertyMapper.java | 2
opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Rest2LdapJsonConfiguratorTest.java | 1
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Resource.java | 81 +++++++++++++++++++++++++++++++++-------
4 files changed, 70 insertions(+), 16 deletions(-)
diff --git a/opendj-bom/pom.xml b/opendj-bom/pom.xml
index 074b7f7..3c28663 100644
--- a/opendj-bom/pom.xml
+++ b/opendj-bom/pom.xml
@@ -45,7 +45,7 @@
<dependency>
<groupId>org.forgerock.commons</groupId>
<artifactId>commons-bom</artifactId>
- <version>21.0.0-alpha-12</version>
+ <version>21.0.0-SNAPSHOT</version>
<scope>import</scope>
<type>pom</type>
</dependency>
diff --git a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Resource.java b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Resource.java
index 1e6dffb..616cdfd 100644
--- a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Resource.java
+++ b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Resource.java
@@ -513,7 +513,9 @@
* @return a new api description that describes a single instance resource.
*/
ApiDescription instanceApi(boolean isReadOnly) {
- if (allProperties.isEmpty()) {
+ if (allProperties.isEmpty() && superType == null && subTypes.isEmpty()) {
+ // It is not used in the api description
+ // so do not generate anything for this resource
return null;
}
@@ -536,7 +538,8 @@
return ApiDescription.apiDescription()
.id("unused").version("unused")
.definitions(definitions())
- .paths(getPaths())
+ .services(services(resource))
+ .paths(paths())
.errors(errors())
.build();
}
@@ -576,15 +579,19 @@
return ApiDescription.apiDescription()
.id("unused").version("unused")
.definitions(definitions())
- .services(Services.services()
- .put(id, resource.build())
- .build())
- .paths(getPaths())
+ .services(services(resource))
+ .paths(paths())
.errors(errors())
.build();
}
- private Paths getPaths() {
+ private Services services(org.forgerock.api.models.Resource.Builder resource) {
+ return Services.services()
+ .put(id, resource.build())
+ .build();
+ }
+
+ private Paths paths() {
return Paths.paths()
// do not put anything in the path to avoid unfortunate string concatenation
// also use UNVERSIONED and rely on the router to stamp the version
@@ -594,13 +601,34 @@
private Definitions definitions() {
final Definitions.Builder definitions = Definitions.definitions();
- definitions.put(id, buildJsonSchema());
- for (Resource subType : subTypes) {
- definitions.put(subType.id, subType.buildJsonSchema());
+ for (Resource res : collectTypeHierarchy(this)) {
+ definitions.put(res.id, res.buildJsonSchema());
}
return definitions.build();
}
+ private static Iterable<Resource> collectTypeHierarchy(Resource currentType) {
+ final List<Resource> typeHierarchy = new ArrayList<>();
+
+ Resource ancestorType = currentType;
+ while (ancestorType.superType != null) {
+ ancestorType = ancestorType.superType;
+ typeHierarchy.add(ancestorType);
+ }
+
+ typeHierarchy.add(currentType);
+
+ addSubTypes(typeHierarchy, currentType);
+ return typeHierarchy;
+ }
+
+ private static void addSubTypes(final List<Resource> typeHierarchy, Resource res) {
+ for (Resource subType : res.subTypes) {
+ typeHierarchy.add(subType);
+ addSubTypes(typeHierarchy, subType);
+ }
+ }
+
private LocalizableString toLS(LocalizableMessage msg) {
if (msg != null) {
// FIXME this code does cannot work today because LocalizableMessage.getKey() does not exist
@@ -782,7 +810,7 @@
field("name", "New Password"),
field("description", "New password as a UTF-8 string."),
field("format", "password")))))));
- return Schema.schema().schema(jsonSchema).build();
+ return schema(jsonSchema);
}
private static org.forgerock.api.models.Action resetPasswordAction() {
@@ -814,13 +842,13 @@
field("generatedPassword", object(
field("type", "string"),
field("description", "Generated password to communicate to the user.")))))));
- return Schema.schema().schema(jsonSchema).build();
+ return schema(jsonSchema);
}
private Schema buildJsonSchema() {
final List<String> requiredFields = new ArrayList<>();
JsonValue properties = json(JsonValue.object());
- for (Map.Entry<String, PropertyMapper> prop : allProperties.entrySet()) {
+ for (Map.Entry<String, PropertyMapper> prop : declaredProperties.entrySet()) {
final String propertyName = prop.getKey();
final PropertyMapper mapper = prop.getValue();
if (mapper.isRequired()) {
@@ -833,13 +861,34 @@
}
final JsonValue jsonSchema = json(object(field("type", "object")));
+ final String discriminator = getDiscriminator();
+ if (discriminator != null) {
+ jsonSchema.put("discriminator", discriminator);
+ }
if (!requiredFields.isEmpty()) {
jsonSchema.put("required", requiredFields);
}
if (properties.size() > 0) {
jsonSchema.put("properties", properties);
}
- return Schema.schema().schema(jsonSchema).build();
+
+ if (superType != null) {
+ return schema(json(object(
+ field("allOf", array(
+ object(field("$ref", "#/definitions/" + superType.id)),
+ jsonSchema)))));
+ }
+ return schema(jsonSchema);
+ }
+
+ private String getDiscriminator() {
+ if (resourceTypeProperty != null) {
+ // Subtypes inherit the resourceTypeProperty from their parent.
+ // The discriminator must only be output for the type that defined it.
+ final String propertyName = resourceTypeProperty.leaf();
+ return declaredProperties.containsKey(propertyName) ? propertyName : null;
+ }
+ return null;
}
private Errors errors() {
@@ -876,6 +925,10 @@
return Schema.schema().reference(ref(referenceValue)).build();
}
+ private static org.forgerock.api.models.Schema schema(JsonValue jsonSchema) {
+ return Schema.schema().schema(jsonSchema).build();
+ }
+
static Reference ref(String referenceValue) {
return Reference.reference().value(referenceValue).build();
}
diff --git a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ResourceTypePropertyMapper.java b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ResourceTypePropertyMapper.java
index d667f91..15e0587 100644
--- a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ResourceTypePropertyMapper.java
+++ b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ResourceTypePropertyMapper.java
@@ -60,7 +60,7 @@
@Override
boolean isRequired() {
- return false;
+ return true;
}
@Override
diff --git a/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Rest2LdapJsonConfiguratorTest.java b/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Rest2LdapJsonConfiguratorTest.java
index 238f4a6..2690bd4 100644
--- a/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Rest2LdapJsonConfiguratorTest.java
+++ b/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Rest2LdapJsonConfiguratorTest.java
@@ -69,6 +69,7 @@
assertThat(api.getVersion()).isEqualTo(VERSION);
assertThat(api.getPaths().getNames()).containsOnly("/api/users", "/api/groups");
assertThat(api.getDefinitions().getNames()).containsOnly(
+ "frapi:opendj:rest2ldap:object:1.0",
"frapi:opendj:rest2ldap:group:1.0",
"frapi:opendj:rest2ldap:user:1.0",
"frapi:opendj:rest2ldap:posixUser:1.0");
--
Gitblit v1.10.0