001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.server.docs;
018
019import org.apache.juneau.bean.openapi3.*;
020import org.apache.juneau.bean.openapi3.ui.*;
021import org.apache.juneau.http.response.*;
022import org.apache.juneau.marshall.*;
023import org.apache.juneau.marshall.html.*;
024import org.apache.juneau.marshall.jsonschema.*;
025import org.apache.juneau.rest.server.*;
026
027/**
028 * Mixin that publishes the Redoc HTML view at {@code /redoc} for the OpenAPI 3.1 spec.
029 *
030 * <p>
031 * Sibling of {@link SwaggerMixin} (Swagger v2 spec at {@code /api}),
032 * {@link SwaggerUiMixin} (HTML-first mount at {@code /swagger}), and {@link OpenApiMixin}
033 * (OpenAPI 3.1 spec at {@code /openapi*}). All four classes live in the
034 * {@code org.apache.juneau.rest.server.docs} api-docs mixin pack.
035 *
036 * <p>
037 * Declares {@link Rest#mixins() @Rest(mixins=&#123;OpenApiMixin.class&#125;)} so transitive resolution
038 * pulls in {@code OpenApiMixin} (and its {@code /openapi*} mounts) automatically when this
039 * mixin is composed into a host.
040 *
041 * <h5 class='section'>Configurable mount path:</h5>
042 *
043 * <p>
044 * The default mount {@code /redoc/*} can be overridden via the SVL variable
045 * {@code ${juneau.redoc.path:redoc}} &mdash; set via system property
046 * ({@code -Djuneau.redoc.path=docs}), environment variable
047 * ({@code JUNEAU_REDOC_PATH=docs}), or {@code Config} key
048 * ({@code juneau.redoc.path = docs}) to change the runtime mount without subclassing.
049 * Resolution happens once at {@link RestContext} construction time (SVL resolution in {@code @RestOp(path)}).
050 *
051 * <p>
052 * Override accepts bare token ({@code redoc}), leading slash ({@code /redoc}), trailing slash
053 * ({@code redoc/}), or wildcard suffix ({@code /redoc/*}) &mdash; all resolve to the same mount.
054 *
055 * <h5 class='section'>Mixin-only deployment:</h5>
056 *
057 * <p>
058 * This resource is designed for composition via {@code @Rest(mixins=...)}. The mount path is
059 * pinned at the op level by {@link RestGet @RestGet(path="/&#123;juneau.redoc.path:redoc&#125;/*")} on
060 * {@link #getRedoc}; a class-level {@code @Rest(paths=...)} declaration would be silently
061 * ignored under the mixin pattern (see {@link Rest#paths() @Rest(paths)} Javadoc).
062 *
063 * <h5 class='figure'>Composition example:</h5>
064 *
065 * <p class='bjava'>
066 *    <ja>@Rest</ja>(path=<js>"/myresource"</js>, mixins=RedocMixin.<jk>class</jk>)
067 *    <jk>public class</jk> MyResource <jk>extends</jk> RestServlet {
068 *       <jc>// Serves /myresource/openapi, /openapi.json, /openapi.yaml (transitively) and /myresource/redoc (this mixin).</jc>
069 *    }
070 * </p>
071 *
072 * <h5 class='section'>Content negotiation:</h5>
073 *
074 * <p>
075 * {@link Rest#defaultAccept() @Rest(defaultAccept="text/html")} causes bare browser requests
076 * (no {@code Accept} header) to render HTML via the {@link RedocUI} swap. Explicit accept headers
077 * (e.g. {@code Accept: application/json}) are honored normally.
078 * </p>
079 *
080 * <h5 class='section'>See Also:</h5><ul>
081 *    <li class='jc'>{@link SwaggerMixin}
082 *    <li class='jc'>{@link SwaggerUiMixin}
083 *    <li class='jc'>{@link OpenApiMixin}
084 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerComposition">REST Server — Mixins and Multi-Mount Paths</a>
085 * </ul>
086 *
087 * @since 10.0.0
088 */
089// @formatter:off
090@Rest(
091   mixins={OpenApiMixin.class},
092   defaultAccept="text/html"
093)
094@JsonSchemaConfig(
095   addDescriptionsTo="bean,collection,array,map,enum",
096   addExamplesTo="bean,collection,array,map",
097   ignoreTypes="OpenApi,org.apache.juneau.bean.html5.*",
098   useBeanDefs="true"
099)
100public class RedocMixin {
101
102   /**
103    * [GET /redoc] &mdash; render the Redoc HTML view (or the spec, depending on {@code Accept}).
104    *
105    * @param req The current REST request.
106    * @return The OpenAPI document.
107    * @throws NotFound If no OpenAPI document is available for this resource.
108    */
109   @RestGet(
110      path="/#{pathToken(${juneau.redoc.path:redoc})}/*",
111      summary="Redoc UI",
112      description="Redoc HTML view of the OpenAPI 3.1 documentation for this resource."
113   )
114   @HtmlDocConfig(
115      rank=10,
116      navlinks={
117         "back: servlet:/",
118         "json: servlet:/openapi.json",
119         "yaml: servlet:/openapi.yaml"
120      },
121      aside="NONE"
122   )
123   @MarshalledConfig(
124      swaps={
125         RedocUI.class
126      }
127   )
128   public OpenApi getRedoc(RestRequest req) {
129      return req.getOpenApi().orElseThrow(NotFound::new);
130   }
131}