blob: 1917ea13a1e9ed0485ea82f99b89f8701211f38e [file] [log] [blame]
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.rest.resources;
17
18import com.fasterxml.jackson.databind.node.ArrayNode;
19import com.fasterxml.jackson.databind.node.ObjectNode;
20import org.onosproject.rest.AbstractInjectionResource;
21import org.onosproject.rest.ApiDocService;
22
23import javax.ws.rs.GET;
24import javax.ws.rs.Path;
25import javax.ws.rs.PathParam;
Thomas Vachuskaaf0ee532015-08-19 14:17:36 -070026import javax.ws.rs.core.Context;
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -070027import javax.ws.rs.core.Response;
Thomas Vachuskaaf0ee532015-08-19 14:17:36 -070028import javax.ws.rs.core.UriInfo;
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -070029import java.io.ByteArrayInputStream;
30import java.io.IOException;
31import java.io.InputStream;
32import java.io.SequenceInputStream;
Thomas Vachuskaaf0ee532015-08-19 14:17:36 -070033import java.net.URI;
34import java.net.URISyntaxException;
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -070035
36import static com.google.common.collect.ImmutableList.of;
37import static com.google.common.io.ByteStreams.toByteArray;
38import static javax.ws.rs.core.MediaType.*;
Thomas Vachuskaaf0ee532015-08-19 14:17:36 -070039import static javax.ws.rs.core.Response.temporaryRedirect;
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -070040import static org.onlab.util.Tools.nullIsNotFound;
41
42/**
43 * REST API documentation.
44 */
45@Path("docs")
46public class ApiDocResource extends AbstractInjectionResource {
47
48 private static final String CONTENT_TYPE = "Content-Type";
Lakshya Thakurf111f572020-11-27 23:42:51 +053049 private static final String CONTENT_SECURITY_POLICY = "Content-Security-Policy";
50 private static final String FRAME_ANCESTORS_NONE = "frame-ancestors 'none'";
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -070051 private static final String STYLESHEET = "text/css";
52 private static final String SCRIPT = "text/javascript";
53 private static final String DOCS = "/docs/";
54
55 private static final String INJECT_START = "<!-- {API-START} -->";
56 private static final String INJECT_END = "<!-- {API-END} -->";
57
Thomas Vachuskaaf0ee532015-08-19 14:17:36 -070058 @Context
59 private UriInfo uriInfo;
60
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -070061 /**
62 * Get all registered REST API docs.
63 * Returns array of all registered API docs.
64 *
65 * @return 200 OK
66 */
67 @GET
68 @Path("apis")
69 public Response getApiList() {
70 ObjectNode root = mapper().createObjectNode();
71 ArrayNode apis = newArray(root, "apis");
72 get(ApiDocService.class).getDocProviders().forEach(p -> apis.add(p.name()));
73 return ok(root.toString()).build();
74 }
75
76 /**
77 * Get Swagger UI JSON.
78 *
79 * @param key REST API web context
80 * @return 200 OK
81 */
82 @GET
83 @Path("apis/{key: .*?}/swagger.json")
84 public Response getApi(@PathParam("key") String key) {
85 String k = key.startsWith("/") ? key : "/" + key;
86 InputStream stream = nullIsNotFound(get(ApiDocService.class).getDocProvider(k),
87 "REST API not found for " + k).docs();
88 return ok(nullIsNotFound(stream, "REST API docs not found for " + k))
89 .header(CONTENT_TYPE, APPLICATION_JSON).build();
90 }
91
92 /**
93 * Get REST API model schema.
94 *
95 * @param key REST API web context
96 * @return 200 OK
97 */
98 @GET
99 @Path("apis/{key: .*?}/model.json")
100 public Response getApiModel(@PathParam("name") String key) {
101 String k = key.startsWith("/") ? key : "/" + key;
102 InputStream stream = nullIsNotFound(get(ApiDocService.class).getDocProvider(k),
103 "REST API not found for " + k).model();
104 return ok(nullIsNotFound(stream, "REST API model not found for " + k))
105 .header(CONTENT_TYPE, APPLICATION_JSON).build();
106 }
107
108 /**
109 * Get Swagger UI main index page.
110 *
111 * @return 200 OK
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700112 * @throws IOException if unable to get index resource
113 * @throws URISyntaxException if unable to create redirect URI
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700114 */
115 @GET
Thomas Vachuskaaf0ee532015-08-19 14:17:36 -0700116 public Response getDefault() throws IOException, URISyntaxException {
117 return uriInfo.getPath().endsWith("/") ? getIndex() :
118 temporaryRedirect(new URI(uriInfo.getPath() + "/")).build();
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700119 }
120
121 /**
122 * Get Swagger UI main index page.
123 *
124 * @return 200 OK
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700125 * @throws IOException if unable to get index resource
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700126 */
127 @GET
128 @Path("index.html")
129 public Response getIndex() throws IOException {
130 InputStream stream = getClass().getClassLoader().getResourceAsStream(DOCS + "index.html");
131 nullIsNotFound(stream, "index.html not found");
132
133 String index = new String(toByteArray(stream));
134
135 int p1s = split(index, 0, INJECT_START);
136 int p1e = split(index, p1s, INJECT_END);
137 int p2s = split(index, p1e, null);
138
139 StreamEnumeration streams =
140 new StreamEnumeration(of(stream(index, 0, p1s),
141 includeOptions(get(ApiDocService.class)),
142 stream(index, p1e, p2s)));
143
144 return ok(new SequenceInputStream(streams))
Lakshya Thakurf111f572020-11-27 23:42:51 +0530145 .header(CONTENT_TYPE, TEXT_HTML)
146 .header(CONTENT_SECURITY_POLICY, FRAME_ANCESTORS_NONE).build();
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700147 }
148
149 private InputStream includeOptions(ApiDocService service) {
150 StringBuilder sb = new StringBuilder();
151 service.getDocProviders().forEach(p -> {
152 sb.append("<option value=\"").append(p.key()).append("\"")
Jon Halla3fcf672017-03-28 16:53:22 -0700153 .append("/onos/v1".equals(p.key()) ? " selected>" : ">")
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700154 .append(p.name())
155 .append("</option>");
156 });
157 return new ByteArrayInputStream(sb.toString().getBytes());
158 }
159
160 /**
161 * Get Swagger UI resource.
162 *
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700163 * @param resource path of the resource
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700164 * @return 200 OK
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700165 * @throws IOException if unable to get named resource
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700166 */
167 @GET
168 @Path("{resource: .*}")
169 public Response getResource(@PathParam("resource") String resource) throws IOException {
Jon Halla3fcf672017-03-28 16:53:22 -0700170 if ("".equals(resource)) {
Jian Li9d616492016-03-09 10:52:49 -0800171 return getIndex();
172 }
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700173 InputStream stream = getClass().getClassLoader().getResourceAsStream(DOCS + resource);
174 return ok(nullIsNotFound(stream, resource + " not found"))
175 .header(CONTENT_TYPE, contentType(resource)).build();
176 }
177
178 static String contentType(String resource) {
179 return resource.endsWith(".html") ? TEXT_HTML :
180 resource.endsWith(".css") ? STYLESHEET :
181 resource.endsWith(".js") ? SCRIPT :
182 APPLICATION_OCTET_STREAM;
183 }
184}