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={OpenApiMixin.class})} 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}} — 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/*}) — 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="/{juneau.redoc.path:redoc}/*")} 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] — 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}