All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.linkedin.restli.docgen.DefaultDocumentationRequestHandler Maven / Gradle / Ivy

/*
   Copyright (c) 2012 LinkedIn Corp.

   Licensed 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 com.linkedin.restli.docgen;

import com.linkedin.data.schema.DataSchemaResolver;
import com.linkedin.data.schema.resolver.ClassNameDataSchemaResolver;
import com.linkedin.jersey.api.uri.UriBuilder;
import com.linkedin.jersey.api.uri.UriComponent;
import com.linkedin.jersey.core.util.MultivaluedMap;
import com.linkedin.r2.message.rest.RestRequest;
import com.linkedin.r2.message.rest.RestResponse;
import com.linkedin.r2.message.rest.RestResponseBuilder;
import com.linkedin.restli.common.HttpMethod;
import com.linkedin.restli.common.HttpStatus;
import com.linkedin.restli.common.RestConstants;
import com.linkedin.restli.internal.server.model.ResourceModel;
import com.linkedin.restli.server.RestLiConfig;
import com.linkedin.restli.server.RestLiDocumentationRequestHandler;
import com.linkedin.restli.server.RoutingException;

import java.io.ByteArrayOutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * Default {@link RestLiDocumentationRequestHandler} that serves both HTML and JSON documentation.
 *
 * @author Keren Jin
 */
public class DefaultDocumentationRequestHandler implements RestLiDocumentationRequestHandler
{
  @Override
  public void initialize(RestLiConfig config, Map rootResources)
  {
    final DataSchemaResolver schemaResolver = new ClassNameDataSchemaResolver();
    final ResourceSchemaCollection resourceSchemas = ResourceSchemaCollection.loadOrCreateResourceSchema(rootResources);
    final RestLiResourceRelationship relationships = new RestLiResourceRelationship(resourceSchemas, schemaResolver);

    _htmlRenderer = new RestLiHTMLDocumentationRenderer(config.getServerNodeUri(),
                                                        relationships,
                                                        new VelocityTemplatingEngine(),
                                                        schemaResolver);
    _jsonRenderer = new RestLiJSONDocumentationRenderer(relationships);
  }

  @Override
  public boolean isDocumentationRequest(RestRequest request)
  {
    final String path = request.getURI().getRawPath();
    final List pathSegments = UriComponent.decodePath(path, true);
    return (pathSegments.size() > 2 &&
                DOC_PREFIX.equals(pathSegments.get(1).getPath()) &&
                DOC_VIEW_DOCS_ACTION.equals(pathSegments.get(2).getPath())) ||
            HttpMethod.valueOf(request.getMethod()) == HttpMethod.OPTIONS;
  }

  @Override
  @SuppressWarnings("fallthrough")
  public RestResponse processDocumentationRequest(RestRequest request)
  {
    final String path = request.getURI().getRawPath();
    final List pathSegments = UriComponent.decodePath(path, true);

    String prefixSegment = null;
    String actionSegment = null;
    String typeSegment = null;
    String objectSegment = null;

    switch (pathSegments.size())
    {
      case 5:
        objectSegment = pathSegments.get(4).getPath();
      case 4:
        typeSegment = pathSegments.get(3).getPath();
      case 3:
        actionSegment = pathSegments.get(2).getPath();
      case 2:
        prefixSegment = pathSegments.get(1).getPath();
    }

    assert(prefixSegment.equals(DOC_PREFIX) || (HttpMethod.valueOf(request.getMethod()) == HttpMethod.OPTIONS));

    final ByteArrayOutputStream out = new ByteArrayOutputStream(BAOS_BUFFER_SIZE);
    final RestLiDocumentationRenderer renderer;

    if (HttpMethod.valueOf(request.getMethod()) == HttpMethod.OPTIONS)
    {
      renderer = _jsonRenderer;
      renderer.renderResource(prefixSegment, out);
    }
    else if (HttpMethod.valueOf(request.getMethod()) == HttpMethod.GET)
    {
      if (!DOC_VIEW_DOCS_ACTION.equals(actionSegment))
      {
        throw createRoutingError(path);
      }

      final MultivaluedMap queryMap = UriComponent.decodeQuery(request.getURI().getQuery(), false);
      final List formatList = queryMap.get("format");
      if (formatList == null)
      {
        renderer = _htmlRenderer;
      }
      else if (formatList.size() > 1)
      {
        throw new RoutingException(
            String.format("\"format\" query parameter must be unique, where multiple are specified: %s",
            Arrays.toString(formatList.toArray())),
            HttpStatus.S_400_BAD_REQUEST.getCode());
      }
      else
      {
        renderer = (formatList.contains(DOC_JSON_FORMAT) ? _jsonRenderer : _htmlRenderer);
      }

      if (renderer == _htmlRenderer)
      {
        _htmlRenderer.setJsonFormatUri(UriBuilder.fromUri(request.getURI())
                                                 .queryParam("format", DOC_JSON_FORMAT)
                                                 .build());
      }

      try
      {
        if (typeSegment == null || typeSegment.isEmpty())
        {
          renderer.renderHome(out);
        }
        else
        {
          if (DOC_RESOURCE_TYPE.equals(typeSegment))
          {
            if (objectSegment == null || objectSegment.isEmpty())
            {
              renderer.renderResourceHome(out);
            }
            else
            {
              renderer.renderResource(objectSegment, out);
            }
          }
          else if (DOC_DATA_TYPE.equals(typeSegment))
          {
            if (objectSegment == null || objectSegment.isEmpty())
            {
              renderer.renderDataModelHome(out);
            }
            else
            {
              renderer.renderDataModel(objectSegment, out);
            }
          }
          else
          {
            throw createRoutingError(path);
          }
        }
      }
      catch (RuntimeException e)
      {
        if (!renderer.handleException(e, out))
        {
          throw e;
        }
      }
    }
    else
    {
      throw new RoutingException(HttpStatus.S_405_METHOD_NOT_ALLOWED.getCode());
    }

    return new RestResponseBuilder().
           setStatus(HttpStatus.S_200_OK.getCode()).
           setHeader(RestConstants.HEADER_CONTENT_TYPE, renderer.getMIMEType()).
           setEntity(out.toByteArray()).
           build();
  }

  private static RoutingException createRoutingError(String path)
  {
    return new RoutingException(String.format("Invalid documentation path %s", path), HttpStatus.S_404_NOT_FOUND.getCode());
  }

  private static final String DOC_PREFIX = "restli";
  private static final String DOC_VIEW_DOCS_ACTION = "docs";
  private static final String DOC_RESOURCE_TYPE = "rest";
  private static final String DOC_DATA_TYPE = "data";
  private static final String DOC_JSON_FORMAT = "json";
  private static final int BAOS_BUFFER_SIZE = 8192;

  private RestLiHTMLDocumentationRenderer _htmlRenderer;
  private RestLiJSONDocumentationRenderer _jsonRenderer;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy