Skip to content

Commit

Permalink
Allow setting example paths and queries for DocService (line#2546)
Browse files Browse the repository at this point in the history
Motivation:

Currently, a user cannot set example paths and queries. line#2195
The endpoint path or queries are only set automatically when
opening a URL that contains `endpoint_path` or `queries` as params.

Modifications:

- Add `examplePaths` to `DocService` and `DocServiceBuilder`
- Add `exampleQueries` to `DocService` and `DocServiceBuilder`
- Show `examplePath` if the `endpoint_path` is null and the `examplePath` is not null
- Show `exampleQuery` if the `queries` is null and the `exampleQuery` is not null
- Make `ENDPOINT PATH` input visible with read-only if a path is exact type 

Result:
You can now set an example path and queries for `DocService`
Fixes line#2195
  • Loading branch information
ikhoon authored Mar 10, 2020
1 parent a5d8484 commit d3e1f9f
Show file tree
Hide file tree
Showing 13 changed files with 408 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ public static DocServiceBuilder builder() {

private final Map<String, ListMultimap<String, HttpHeaders>> exampleHttpHeaders;
private final Map<String, ListMultimap<String, String>> exampleRequests;
private final Map<String, ListMultimap<String, String>> examplePaths;
private final Map<String, ListMultimap<String, String>> exampleQueries;
private final DocServiceFilter filter;

@Nullable
Expand All @@ -108,14 +110,18 @@ public static DocServiceBuilder builder() {
* Creates a new instance.
*/
public DocService() {
this(ImmutableMap.of(), ImmutableMap.of(), ImmutableList.of(), DocServiceBuilder.ALL_SERVICES);
this(/* exampleHttpHeaders */ ImmutableMap.of(), /* exampleRequests */ ImmutableMap.of(),
/* examplePaths */ ImmutableMap.of(), /* exampleQueries */ ImmutableMap.of(),
/* injectedScriptSuppliers */ ImmutableList.of(), DocServiceBuilder.ALL_SERVICES);
}

/**
* Creates a new instance with example HTTP headers and example requests and injected scripts.
*/
DocService(Map<String, ListMultimap<String, HttpHeaders>> exampleHttpHeaders,
Map<String, ListMultimap<String, String>> exampleRequests,
Map<String, ListMultimap<String, String>> examplePaths,
Map<String, ListMultimap<String, String>> exampleQueries,
List<BiFunction<ServiceRequestContext, HttpRequest, String>> injectedScriptSuppliers,
DocServiceFilter filter) {

Expand All @@ -130,6 +136,8 @@ public DocService() {
"com/linecorp/armeria/server/docs")));
this.exampleHttpHeaders = immutableCopyOf(exampleHttpHeaders, "exampleHttpHeaders");
this.exampleRequests = immutableCopyOf(exampleRequests, "exampleRequests");
this.examplePaths = immutableCopyOf(examplePaths, "examplePaths");
this.exampleQueries = immutableCopyOf(exampleQueries, "exampleQueries");
this.filter = requireNonNull(filter, "filter");
}

Expand Down Expand Up @@ -235,6 +243,8 @@ private static MethodInfo addMethodDocStrings(ServiceInfo service, MethodInfo me
method.endpoints(),
method.exampleHttpHeaders(),
method.exampleRequests(),
method.examplePaths(),
method.exampleQueries(),
method.httpMethod(),
docString(service.name() + '/' + method.name(), method.docString(), docStrings));
}
Expand Down Expand Up @@ -311,6 +321,10 @@ private ServiceInfo addServiceExamples(ServiceInfo service) {
this.exampleHttpHeaders.getOrDefault(service.name(), ImmutableListMultimap.of());
final ListMultimap<String, String> exampleRequests =
this.exampleRequests.getOrDefault(service.name(), ImmutableListMultimap.of());
final ListMultimap<String, String> examplePaths =
this.examplePaths.getOrDefault(service.name(), ImmutableListMultimap.of());
final ListMultimap<String, String> exampleQueries =
this.exampleQueries.getOrDefault(service.name(), ImmutableListMultimap.of());

// Reconstruct ServiceInfo with the examples.
return new ServiceInfo(
Expand All @@ -323,6 +337,8 @@ private ServiceInfo addServiceExamples(ServiceInfo service) {
// generated by the plugin.
concatAndDedup(exampleHttpHeaders.get(m.name()), m.exampleHttpHeaders()),
concatAndDedup(exampleRequests.get(m.name()), m.exampleRequests()),
examplePaths.get(m.name()),
exampleQueries.get(m.name()),
m.httpMethod(), m.docString()))::iterator,
Iterables.concat(service.exampleHttpHeaders(),
exampleHttpHeaders.get("")),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public final class DocServiceBuilder {

private final Map<String, ListMultimap<String, HttpHeaders>> exampleHttpHeaders = new HashMap<>();
private final Map<String, ListMultimap<String, String>> exampleRequests = new HashMap<>();
private final Map<String, ListMultimap<String, String>> examplePaths = new HashMap<>();
private final Map<String, ListMultimap<String, String>> exampleQueries = new HashMap<>();
private final List<BiFunction<ServiceRequestContext, HttpRequest, String>> injectedScriptSuppliers =
new ArrayList<>();

Expand Down Expand Up @@ -177,6 +179,90 @@ private DocServiceBuilder exampleHttpHeaders0(String serviceName, String methodN
return this;
}

/**
* Adds the specified example paths for the method with the specified service and method name.
*/
public DocServiceBuilder examplePaths(Class<?> serviceType, String methodName, String... paths) {
requireNonNull(serviceType, "serviceType");
return examplePaths(serviceType.getName(), methodName, paths);
}

/**
* Adds the specified example paths for the method with the specified service and method name.
*/
public DocServiceBuilder examplePaths(Class<?> serviceType, String methodName, Iterable<String> paths) {
requireNonNull(serviceType, "serviceType");
return examplePaths(serviceType.getName(), methodName, paths);
}

/**
* Adds the specified example paths for the method with the specified service and method name.
*/
public DocServiceBuilder examplePaths(String serviceName, String methodName, String... paths) {
requireNonNull(paths, "paths");
return examplePaths(serviceName, methodName, ImmutableList.copyOf(paths));
}

/**
* Adds the specified example paths for the method with the specified service and method name.
*/
public DocServiceBuilder examplePaths(String serviceName, String methodName, Iterable<String> paths) {
requireNonNull(serviceName, "serviceName");
checkArgument(!serviceName.isEmpty(), "serviceName is empty.");
requireNonNull(methodName, "methodName");
checkArgument(!methodName.isEmpty(), "methodName is empty.");
requireNonNull(paths, "paths");
for (String examplePath : paths) {
requireNonNull(examplePath, "paths contains null");
examplePaths.computeIfAbsent(serviceName, unused -> ArrayListMultimap.create())
.put(methodName, examplePath);
}
return this;
}

/**
* Adds the specified example query strings for the method with the specified service and method name.
*/
public DocServiceBuilder exampleQueries(Class<?> serviceType, String methodName, String... queryStrings) {
requireNonNull(serviceType, "serviceType");
return exampleQueries(serviceType.getName(), methodName, queryStrings);
}

/**
* Adds the specified example query strings for the method with the specified service and method name.
*/
public DocServiceBuilder exampleQueries(Class<?> serviceType, String methodName,
Iterable<String> queryStrings) {
requireNonNull(serviceType, "serviceType");
return exampleQueries(serviceType.getName(), methodName, queryStrings);
}

/**
* Adds the specified example query strings for the method with the specified service and method name.
*/
public DocServiceBuilder exampleQueries(String serviceName, String methodName, String... queryStrings) {
requireNonNull(queryStrings, "queryStrings");
return exampleQueries(serviceName, methodName, ImmutableList.copyOf(queryStrings));
}

/**
* Adds the specified example query strings for the method with the specified service and method name.
*/
public DocServiceBuilder exampleQueries(String serviceName, String methodName,
Iterable<String> queryStrings) {
requireNonNull(serviceName, "serviceName");
checkArgument(!serviceName.isEmpty(), "serviceName is empty.");
requireNonNull(methodName, "methodName");
checkArgument(!methodName.isEmpty(), "methodName is empty.");
requireNonNull(queryStrings, "queryStrings");
for (String query : queryStrings) {
requireNonNull(query, "queryStrings contains null");
exampleQueries.computeIfAbsent(serviceName, unused -> ArrayListMultimap.create())
.put(methodName, query);
}
return this;
}

/**
* Adds the example requests for the method with the specified service type and method name.
* This method is a shortcut to:
Expand Down Expand Up @@ -446,7 +532,7 @@ private static String[] guessAndSerializeExampleRequest(Object exampleRequest) {
* Returns a newly-created {@link DocService} based on the properties of this builder.
*/
public DocService build() {
return new DocService(exampleHttpHeaders, exampleRequests, injectedScriptSuppliers,
unifyFilter(includeFilter, excludeFilter));
return new DocService(exampleHttpHeaders, exampleRequests, examplePaths, exampleQueries,
injectedScriptSuppliers, unifyFilter(includeFilter, excludeFilter));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.linecorp.armeria.server.docs;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNull;

Expand All @@ -32,10 +33,12 @@
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;

import com.linecorp.armeria.common.HttpHeaders;
import com.linecorp.armeria.common.HttpMethod;
import com.linecorp.armeria.common.util.UnstableApi;
import com.linecorp.armeria.internal.common.PathAndQuery;
import com.linecorp.armeria.server.Service;

/**
Expand All @@ -53,6 +56,8 @@ public final class MethodInfo {
private final Set<EndpointInfo> endpoints;
private final List<HttpHeaders> exampleHttpHeaders;
private final List<String> exampleRequests;
private final List<String> examplePaths;
private final List<String> exampleQueries;
private final HttpMethod httpMethod;
@Nullable
private final String docString;
Expand All @@ -78,8 +83,10 @@ public MethodInfo(String name,
Iterable<EndpointInfo> endpoints,
HttpMethod httpMethod,
@Nullable String docString) {
this(name, returnTypeSignature, parameters, exceptionTypeSignatures,
endpoints, ImmutableList.of(), ImmutableList.of(), httpMethod, docString);
this(name, returnTypeSignature, parameters, exceptionTypeSignatures, endpoints,
/* exampleHttpHeaders */ ImmutableList.of(), /* exampleRequests */ ImmutableList.of(),
/* examplePaths */ ImmutableList.of(), /* exampleQueries */ ImmutableList.of(),
httpMethod, docString);
}

/**
Expand All @@ -92,6 +99,8 @@ public MethodInfo(String name,
Iterable<EndpointInfo> endpoints,
Iterable<HttpHeaders> exampleHttpHeaders,
Iterable<String> exampleRequests,
Iterable<String> examplePaths,
Iterable<String> exampleQueries,
HttpMethod httpMethod,
@Nullable String docString) {
this.name = requireNonNull(name, "name");
Expand All @@ -108,6 +117,27 @@ public MethodInfo(String name,
this.exampleHttpHeaders = ImmutableList.copyOf(requireNonNull(exampleHttpHeaders,
"exampleHttpHeaders"));
this.exampleRequests = ImmutableList.copyOf(requireNonNull(exampleRequests, "exampleRequests"));

requireNonNull(examplePaths, "examplePaths");
final ImmutableList.Builder<String> examplePathsBuilder =
ImmutableList.builderWithExpectedSize(Iterables.size(examplePaths));
for (String path : examplePaths) {
final PathAndQuery pathAndQuery = PathAndQuery.parse(path);
checkArgument(pathAndQuery != null, "examplePaths contains an invalid path: %s", path);
examplePathsBuilder.add(pathAndQuery.path());
}
this.examplePaths = examplePathsBuilder.build();

requireNonNull(exampleQueries, "exampleQueries");
final ImmutableList.Builder<String> exampleQueriesBuilder =
ImmutableList.builderWithExpectedSize(Iterables.size(exampleQueries));
for (String query : exampleQueries) {
final PathAndQuery pathAndQuery = PathAndQuery.parse('?' + query);
checkArgument(pathAndQuery != null, "exampleQueries contains an invalid query string: %s", query);
exampleQueriesBuilder.add(pathAndQuery.query());
}
this.exampleQueries = exampleQueriesBuilder.build();

this.httpMethod = requireNonNull(httpMethod, "httpMethod");
this.docString = Strings.emptyToNull(docString);
}
Expand Down Expand Up @@ -169,6 +199,22 @@ public List<String> exampleRequests() {
return exampleRequests;
}

/**
* Returns the example paths of the method.
*/
@JsonProperty
public List<String> examplePaths() {
return examplePaths;
}

/**
* Returns the example queries of the method.
*/
@JsonProperty
public List<String> exampleQueries() {
return exampleQueries;
}

/**
* Returns the HTTP method of this method.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@ static Set<MethodInfo> mergeEndpoints(Iterable<MethodInfo> methodInfos) {
return new MethodInfo(value.name(), value.returnTypeSignature(),
value.parameters(), value.exceptionTypeSignatures(),
endpointInfos, value.exampleHttpHeaders(),
value.exampleRequests(), value.httpMethod(),
value.docString());
value.exampleRequests(), value.examplePaths(), value.exampleQueries(),
value.httpMethod(), value.docString());
}
});
}
Expand Down
Loading

0 comments on commit d3e1f9f

Please sign in to comment.