opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Resource.java
@@ -60,6 +60,7 @@ import org.forgerock.api.models.Services; import org.forgerock.api.models.Update; import org.forgerock.http.ApiProducer; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.LocalizedIllegalArgumentException; import org.forgerock.json.JsonPointer; import org.forgerock.json.JsonValue; @@ -128,12 +129,23 @@ private volatile Boolean hasSubTypesWithSubResources = null; /** The set of actions supported by this resource and its sub-types. */ private final Set<Action> supportedActions = new HashSet<>(); private LocalizableMessage description; Resource(final String id) { this.id = id; } /** * Sets the description of this resource. * * @param description * the description of this resource */ public void description(LocalizableMessage description) { this.description = description; } /** * Returns the resource ID of this resource. * * @return The resource ID of this resource. @@ -507,6 +519,8 @@ org.forgerock.api.models.Resource.Builder resource = org.forgerock.api.models.Resource. resource() .title(id) .description(toLS(description)) .resourceSchema(schemaRef("#/definitions/" + id)) .mvccSupported(isMvccSupported()); @@ -522,6 +536,7 @@ return ApiDescription.apiDescription() .id("unused").version("unused") .definitions(definitions()) .paths(getPaths()) .errors(errors()) .build(); } @@ -536,6 +551,8 @@ ApiDescription collectionApi(boolean isReadOnly) { org.forgerock.api.models.Resource.Builder resource = org.forgerock.api.models.Resource. resource() .title(id) .description(toLS(description)) .resourceSchema(schemaRef("#/definitions/" + id)) .mvccSupported(isMvccSupported()); @@ -584,6 +601,16 @@ return definitions.build(); } private LocalizableString toLS(LocalizableMessage msg) { if (msg != null) { if (msg.resourceName() != null) { return new LocalizableString("i18n:" + msg.resourceName() + "#" /* TODO + msg.getKey() */); } return new LocalizableString(msg.toString()); } return null; } /** * Returns the api description that describes a resource with sub resources. * opendj-server-legacy/src/main/java/org/opends/server/protocols/http/rest2ldap/AdminEndpoint.java
@@ -12,14 +12,12 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2016 ForgeRock AS. * */ package org.opends.server.protocols.http.rest2ldap; import static org.forgerock.http.routing.RouteMatchers.newResourceApiVersionBehaviourManager; import static org.forgerock.http.routing.Version.version; import static org.forgerock.json.resource.RouteMatchers.resourceApiVersionContextFilter; import static org.forgerock.json.resource.http.CrestHttp.newHttpHandler; import static org.forgerock.opendj.ldap.schema.CoreSchema.getBooleanSyntax; import static org.forgerock.opendj.ldap.schema.CoreSchema.getIntegerSyntax; import static org.forgerock.opendj.rest2ldap.Rest2Ldap.*; @@ -43,11 +41,15 @@ import org.forgerock.http.routing.Version; import org.forgerock.json.JsonPointer; import org.forgerock.json.resource.BadRequestException; import org.forgerock.json.resource.ConnectionFactory; import org.forgerock.json.resource.CrestApplication; import org.forgerock.json.resource.FilterChain; import org.forgerock.json.resource.Request; import org.forgerock.json.resource.RequestHandler; import org.forgerock.json.resource.ResourceException; import org.forgerock.json.resource.Resources; import org.forgerock.json.resource.Router; import org.forgerock.json.resource.http.CrestHttp; import org.forgerock.opendj.config.AbstractManagedObjectDefinition; import org.forgerock.opendj.config.AggregationPropertyDefinition; import org.forgerock.opendj.config.DefaultBehaviorProvider; @@ -84,6 +86,7 @@ import org.opends.server.api.HttpEndpoint; import org.opends.server.core.ServerContext; import org.opends.server.types.InitializationException; import org.opends.server.util.BuildVersion; /** * An HTTP endpoint providing access to the server's monitoring backend (cn=monitor) and its configuration (cn=config). @@ -115,9 +118,7 @@ return new AdminHttpApplication(); } /** * Specialized {@link HttpApplication} using internal connections to this local LDAP server. */ /** Specialized {@link HttpApplication} using internal connections to this local LDAP server. */ private final class AdminHttpApplication implements HttpApplication { private LDAPProfile ldapProfile = LDAPProfile.getInstance(); @@ -125,6 +126,11 @@ @Override public Handler start() throws HttpApplicationException { return newHttpHandler(startRequestHandler()); } FilterChain startRequestHandler() throws HttpApplicationException { final Map<String, Resource> resources = new HashMap<>(); // Define the entry point to the admin API. @@ -196,12 +202,41 @@ // FIXME: Disable the warning header for now due to CREST-389 / CREST-390. final ResourceApiVersionBehaviourManager behaviourManager = newResourceApiVersionBehaviourManager(); behaviourManager.setWarningEnabled(false); return newHttpHandler(new FilterChain(versionRouter, resourceApiVersionContextFilter(behaviourManager))); return new FilterChain(versionRouter, resourceApiVersionContextFilter(behaviourManager)); } private Handler newHttpHandler(RequestHandler handler) { final org.forgerock.json.resource.ConnectionFactory factory = Resources.newInternalConnectionFactory(handler); return CrestHttp.newHttpHandler(new CrestApplication() { @Override public ConnectionFactory getConnectionFactory() { return factory; } @Override public String getApiId() { return "frapi:opendj:admin"; } @Override public String getApiVersion() { return BuildVersion.binaryVersion().toStringNoRevision(); } }); } private Resource buildResource(final AbstractManagedObjectDefinition<?, ?> mod) { final Resource resource = resource(mod.getName()); if (!mod.isTop()) { resource.description(mod.getSynopsis()); } configureResourceProperties(mod, resource); configureResourceSubResources(mod, resource, false); return resource; opendj-server-legacy/src/test/java/org/forgerock/opendj/rest2ldap/AdminEndpointTestCase.java
New file @@ -0,0 +1,124 @@ /* * The contents of this file are subject to the terms of the Common Development and * Distribution License (the License). You may not use this file except in compliance with the * License. * * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions Copyright [year] [name of copyright owner]". * * Portions Copyright 2016 ForgeRock AS. */ package org.forgerock.opendj.rest2ldap; import static org.assertj.core.api.Assertions.*; import static org.forgerock.http.util.Json.*; import static org.forgerock.json.resource.Requests.*; import static org.forgerock.json.resource.ResourcePath.*; import static org.forgerock.opendj.rest2ldap.Rest2Ldap.*; import static org.forgerock.util.Options.*; import java.io.StringReader; import java.lang.reflect.Method; import java.util.Collections; import org.forgerock.api.CrestApiProducer; import org.forgerock.api.models.ApiDescription; import org.forgerock.http.HttpApplication; import org.forgerock.http.routing.UriRouterContext; import org.forgerock.http.util.Json; import org.forgerock.json.JsonValue; import org.forgerock.json.resource.FilterChain; import org.forgerock.json.resource.Request; import org.forgerock.services.context.Context; import org.forgerock.services.context.RootContext; import org.opends.server.DirectoryServerTestCase; import org.opends.server.TestCaseUtils; import org.opends.server.core.DirectoryServer; import org.opends.server.protocols.http.rest2ldap.AdminEndpoint; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; @SuppressWarnings("javadoc") public class AdminEndpointTestCase extends DirectoryServerTestCase { private static final String ID = "frapi:opendj:admin"; private static final String VERSION = "4.0.0"; @BeforeClass public void startServer() throws Exception { TestCaseUtils.startServer(); } @Test public void testApiDescriptionGeneration() throws Exception { FilterChain endpointHandler = configureEndpoint(); final ApiDescription api = requestApi(endpointHandler, "admin/config"); assertThat(api).isNotNull(); // Ensure we can can pretty print and parse back the generated api description parseJson(prettyPrint(api)); assertThat(api.getId()).isEqualTo(ID + ":1.0"); assertThat(api.getVersion()).isEqualTo(VERSION); assertThat(api.getPaths().getNames().size()).isGreaterThan(20); assertThat(api.getDefinitions().getNames().size()).isGreaterThan(150); } private FilterChain configureEndpoint() throws Exception { AdminEndpoint adminEndpoint = new AdminEndpoint(null, DirectoryServer.getInstance().getServerContext()); HttpApplication httpApp = adminEndpoint.newHttpApplication(); FilterChain handler = startRequestHandler(httpApp); handler.api(new CrestApiProducer(ID, VERSION)); return handler; } private FilterChain startRequestHandler(HttpApplication httpApp) throws Exception { Method m = httpApp.getClass().getDeclaredMethod("startRequestHandler"); m.setAccessible(true); FilterChain handler = (FilterChain) m.invoke(httpApp); return handler; } private ApiDescription requestApi(final FilterChain filterChain, String uriPath) { Context context = newRouterContext(uriPath); Request request = newApiRequest(resourcePath(uriPath)); return filterChain.handleApiRequest(context, request); } private Context newRouterContext(final String uriPath) { Context ctx = new RootContext(); ctx = new Rest2LdapContext(ctx, rest2Ldap(defaultOptions())); ctx = new UriRouterContext(ctx, null, uriPath, Collections.<String, String> emptyMap()); return ctx; } private String prettyPrint(Object o) throws Exception { final ObjectMapper objectMapper = new ObjectMapper().registerModules(new Json.LocalizableStringModule(), new Json.JsonValueModule()); final ObjectWriter writer = objectMapper.writer().withDefaultPrettyPrinter(); return writer.writeValueAsString(o); } static JsonValue parseJson(final String json) throws Exception { try (StringReader r = new StringReader(json)) { return new JsonValue(readJsonLenient(r)); } } }