Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support markdown when using @Description Annotation #4304

Merged
merged 58 commits into from
Jul 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
2fc82de
Support markdown when using @description Annotation
cnabro Jun 18, 2022
ba26d58
Show path mappings in Services
cnabro Jun 19, 2022
d61e7fa
Support enums documentation in annotated service
cnabro Jun 19, 2022
f1dce52
Modify AnnotatedDocServicePlugin to support documentation for annotat…
cnabro Jun 19, 2022
e723d3b
Add 'MarkdownDescriptionService' examples for 'annotated-http-service…
cnabro Jun 19, 2022
971f5cc
Add @ResponseObject to specify which element should be converted to s…
cnabro Jun 19, 2022
8964bbe
Fix lints
cnabro Jun 19, 2022
01e6f0b
Modify 'server-docservice.mdx' for annotated doc service
cnabro Jun 19, 2022
e014931
Update core/src/main/java/com/linecorp/armeria/internal/server/annota…
cnabro Jun 22, 2022
7682435
Update core/src/main/java/com/linecorp/armeria/server/annotation/Resp…
cnabro Jun 22, 2022
8305400
Remove unnecessary comment (specification.tsx)
cnabro Jun 22, 2022
6122f61
Fix 'server-docservice.mdx' for Not supported String template.
cnabro Jun 22, 2022
df15acb
Rollback @ResponseObject
cnabro Jun 23, 2022
938e523
Add testcase for AnnotatedDocService
cnabro Jun 23, 2022
8b8d9c8
Fix lints
cnabro Jun 23, 2022
5f9f182
Wrap 'Table' components with 'TableContainer'
cnabro Jun 24, 2022
d0825fb
Add a supported markup type to @Description.
cnabro Jun 24, 2022
68b9bdb
Fix lints
cnabro Jun 24, 2022
7b90860
Fix descriptionInfo for developer guide and some lints.
cnabro Jun 27, 2022
691e4f7
Fix MarkdownDescriptionService examples
cnabro Jun 27, 2022
15cfb5b
Integrate 'DocString' and 'Markup' to 'DescriptionInfo'
cnabro Jun 28, 2022
8709235
Remove unnecessary parameter
cnabro Jun 29, 2022
3ba9cb9
Update core/src/main/java/com/linecorp/armeria/server/docs/DocService…
cnabro Jun 29, 2022
0e92ebf
Update docs-client/src/containers/App/index.tsx
cnabro Jun 29, 2022
79cf683
Fix js lints options
cnabro Jun 29, 2022
f34f9d8
Add dependency @types/mermaid
cnabro Jun 29, 2022
258aafa
Rollback for invalid codes
cnabro Jun 29, 2022
498e123
Change server-docservice for annotated service.
cnabro Jun 29, 2022
c0f08b7
Add type hints for mermaidAPI
cnabro Jul 6, 2022
38e289a
Add licence for description service
cnabro Jul 6, 2022
43466bd
Update site/src/pages/docs/server-docservice.mdx
cnabro Jul 13, 2022
a6dd529
Revert requireNonNull field
cnabro Jul 13, 2022
c3470dc
Update core/src/main/java/com/linecorp/armeria/server/docs/FieldInfoB…
cnabro Jul 18, 2022
d6a3996
Update core/src/main/java/com/linecorp/armeria/server/docs/FieldInfo.…
cnabro Jul 18, 2022
ee9b92f
Update core/src/main/java/com/linecorp/armeria/server/docs/StructInfo…
cnabro Jul 18, 2022
4e375d9
Update core/src/test/java/com/linecorp/armeria/internal/server/annota…
cnabro Jul 18, 2022
8ffd0a3
Update core/src/main/java/com/linecorp/armeria/internal/server/annota…
cnabro Jul 20, 2022
82eb4a4
Update core/src/main/java/com/linecorp/armeria/internal/server/annota…
cnabro Jul 20, 2022
7eefbde
Update core/src/main/java/com/linecorp/armeria/server/annotation/Desc…
cnabro Jul 20, 2022
340ed8a
Update docs-client/src/components/Description/index.tsx
cnabro Jul 20, 2022
d7e1446
Update docs-client/src/components/Description/index.tsx
cnabro Jul 20, 2022
908d9a1
Update core/src/main/java/com/linecorp/armeria/server/docs/NamedTypeI…
cnabro Jul 20, 2022
12ee0ef
Update core/src/main/java/com/linecorp/armeria/server/docs/MethodInfo…
cnabro Jul 20, 2022
87f9094
Update core/src/test/java/com/linecorp/armeria/internal/server/annota…
cnabro Jul 20, 2022
2c21197
Update core/src/main/java/com/linecorp/armeria/server/docs/EnumInfo.java
cnabro Jul 20, 2022
62c273e
Update core/src/main/java/com/linecorp/armeria/server/annotation/Desc…
cnabro Jul 20, 2022
ee8a068
Update core/src/main/java/com/linecorp/armeria/server/docs/StructInfo…
cnabro Jul 20, 2022
00a3145
Update core/src/main/java/com/linecorp/armeria/server/annotation/Desc…
cnabro Jul 20, 2022
76db094
Change DescriptionInfo package
cnabro Jul 20, 2022
db3ab11
Fix typescript lint error
cnabro Jul 20, 2022
d2f2b3e
Fix scala lint
cnabro Jul 20, 2022
fe4cfc1
Update core/src/main/java/com/linecorp/armeria/server/docs/Markup.java
cnabro Jul 20, 2022
923bd05
Update core/src/main/java/com/linecorp/armeria/server/docs/Descriptio…
cnabro Jul 20, 2022
6ff92e5
Update site/src/pages/docs/server-docservice.mdx
cnabro Jul 20, 2022
1b6a75d
Update core/src/main/java/com/linecorp/armeria/server/docs/Descriptio…
cnabro Jul 20, 2022
685af5e
Fix lints
cnabro Jul 20, 2022
51de424
Fix error when descriptionInfo is undefined.
cnabro Jul 26, 2022
5e465ff
Fix site render error
cnabro Jul 26, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.fasterxml.jackson.core.JsonProcessingException;
Expand All @@ -61,14 +60,17 @@
import com.linecorp.armeria.server.RoutePathType;
import com.linecorp.armeria.server.Service;
import com.linecorp.armeria.server.ServiceConfig;
import com.linecorp.armeria.server.annotation.Description;
import com.linecorp.armeria.server.annotation.Header;
import com.linecorp.armeria.server.annotation.Param;
import com.linecorp.armeria.server.annotation.RequestObject;
import com.linecorp.armeria.server.docs.DescriptionInfo;
import com.linecorp.armeria.server.docs.DocServiceFilter;
import com.linecorp.armeria.server.docs.DocServicePlugin;
import com.linecorp.armeria.server.docs.EndpointInfo;
import com.linecorp.armeria.server.docs.EndpointInfoBuilder;
import com.linecorp.armeria.server.docs.EnumInfo;
import com.linecorp.armeria.server.docs.EnumValueInfo;
import com.linecorp.armeria.server.docs.FieldInfo;
import com.linecorp.armeria.server.docs.FieldInfoBuilder;
import com.linecorp.armeria.server.docs.FieldLocation;
Expand Down Expand Up @@ -128,7 +130,7 @@ public ServiceSpecification generateSpecification(Set<ServiceConfig> serviceConf
requireNonNull(filter, "filter");

final Map<Class<?>, Set<MethodInfo>> methodInfos = new HashMap<>();
final Map<Class<?>, String> serviceDescription = new HashMap<>();
final Map<Class<?>, DescriptionInfo> serviceDescription = new HashMap<>();
serviceConfigs.forEach(sc -> {
final AnnotatedService service = sc.service().as(AnnotatedService.class);
if (service != null) {
Expand All @@ -145,7 +147,7 @@ public ServiceSpecification generateSpecification(Set<ServiceConfig> serviceConf
return generate(serviceDescription, methodInfos);
}

private static void addServiceDescription(Map<Class<?>, String> serviceDescription,
private static void addServiceDescription(Map<Class<?>, DescriptionInfo> serviceDescription,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This method will be changed by #4322.
I'd like to ask @cnabro for a review after they are merged.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I'll wait for it 👍

AnnotatedService service) {
final Class<?> clazz = service.object().getClass();
serviceDescription.computeIfAbsent(clazz, AnnotatedServiceFactory::findDescription);
Expand All @@ -164,8 +166,9 @@ private static void addMethodInfo(Map<Class<?>, Set<MethodInfo>> methodInfos,
httpMethod -> {
final MethodInfo methodInfo = new MethodInfo(
name, returnTypeSignature, fieldInfos, ImmutableList.of(), // Ignore exceptions.
ImmutableList.of(endpoint), httpMethod, AnnotatedServiceFactory
.findDescription(method));
ImmutableList.of(endpoint), httpMethod,
AnnotatedServiceFactory.findDescription(method));

methodInfos.computeIfAbsent(clazz, unused -> new HashSet<>()).add(methodInfo);
});
}
Expand Down Expand Up @@ -284,8 +287,9 @@ private static FieldInfo fieldInfo(AnnotatedValueResolver resolver) {
.location(location(resolver))
.requirement(resolver.shouldExist() ? FieldRequirement.REQUIRED
: FieldRequirement.OPTIONAL);
if (resolver.description() != null) {
builder.docString(resolver.description());
final DescriptionInfo description = resolver.description();
if (description != null) {
builder.descriptionInfo(description);
}
return builder.build();
}
Expand Down Expand Up @@ -371,6 +375,10 @@ static TypeSignature toTypeSignature(Type type) {
return TypeSignature.ofList(toTypeSignature(clazz.getComponentType()));
}

if (clazz.isEnum()) {
return TypeSignature.ofNamed(clazz);
}

return TypeSignature.ofBase(clazz.getSimpleName());
}

Expand All @@ -388,7 +396,7 @@ private static FieldLocation location(AnnotatedValueResolver resolver) {
}

@VisibleForTesting
static ServiceSpecification generate(Map<Class<?>, String> serviceDescription,
static ServiceSpecification generate(Map<Class<?>, DescriptionInfo> serviceDescription,
Map<Class<?>, Set<MethodInfo>> methodInfos) {
final Set<ServiceInfo> serviceInfos = methodInfos
.entrySet().stream()
Expand All @@ -409,22 +417,64 @@ private static NamedTypeInfo newNamedTypeInfo(TypeSignature typeSignature) {
}

if (type.isEnum()) {
@SuppressWarnings("unchecked")
final Class<? extends Enum<?>> enumType = (Class<? extends Enum<?>>) type;
return new EnumInfo(enumType);
return newEnumInfo(type);
}

return newStructInfo(type);
}

private static EnumInfo newEnumInfo(Class<?> enumClass) {
final String name = enumClass.getName();
final Description description = AnnotationUtil.findFirst(enumClass, Description.class);

final Field[] declaredFields = enumClass.getDeclaredFields();
final List<EnumValueInfo> values =
Stream.of(declaredFields)
.filter(Field::isEnumConstant)
.map(f -> {
final Description valueDescription = AnnotationUtil.findFirst(f, Description.class);
if (valueDescription != null) {
return new EnumValueInfo(f.getName(), null,
DescriptionInfo.of(valueDescription.value(),
valueDescription.markup()));
}

return new EnumValueInfo(f.getName(), null);
})
.collect(toImmutableList());

if (description != null) {
return new EnumInfo(name, values, DescriptionInfo.of(description.value(), description.markup()));
}

return new EnumInfo(name, values);
}

private static StructInfo newStructInfo(Class<?> structClass) {
final String name = structClass.getName();
final Description description = AnnotationUtil.findFirst(structClass, Description.class);

final Field[] declaredFields = structClass.getDeclaredFields();
final List<FieldInfo> fields =
Stream.of(declaredFields)
.map(f -> FieldInfo.of(f.getName(), toTypeSignature(f.getGenericType())))
.collect(Collectors.toList());
.map(f -> {
final Description fieldDescription = AnnotationUtil.findFirst(f, Description.class);
if (fieldDescription != null) {
return FieldInfo.of(
f.getName(),
toTypeSignature(f.getGenericType()),
DescriptionInfo.of(fieldDescription.value(), fieldDescription.markup())
);
}

return FieldInfo.of(f.getName(), toTypeSignature(f.getGenericType()));
})
.collect(toImmutableList());

if (description != null) {
return new StructInfo(name, fields, DescriptionInfo.of(description.value(), description.markup()));
}

return new StructInfo(name, fields);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
import com.linecorp.armeria.server.annotation.ResponseConverterFunction;
import com.linecorp.armeria.server.annotation.StatusCode;
import com.linecorp.armeria.server.annotation.Trace;
import com.linecorp.armeria.server.docs.DescriptionInfo;

/**
* Builds a list of {@link AnnotatedService}s from an {@link Object}.
Expand Down Expand Up @@ -582,14 +583,14 @@ private static <T extends Annotation, R> Builder<R> getAnnotatedInstances(
* Returns the description of the specified {@link AnnotatedElement}.
*/
@Nullable
cnabro marked this conversation as resolved.
Show resolved Hide resolved
static String findDescription(AnnotatedElement annotatedElement) {
static DescriptionInfo findDescription(AnnotatedElement annotatedElement) {
requireNonNull(annotatedElement, "annotatedElement");
final Description description = AnnotationUtil.findFirst(annotatedElement, Description.class);
if (description != null) {
final String value = description.value();
if (DefaultValues.isSpecified(value)) {
checkArgument(!value.isEmpty(), "value is empty.");
return value;
return DescriptionInfo.of(description.value(), description.markup());
}
} else if (annotatedElement instanceof Parameter) {
// JavaDoc/KDoc descriptions only exist for method parameters
Expand All @@ -600,7 +601,8 @@ static String findDescription(AnnotatedElement annotatedElement) {
final String propertyName = executable.getName() + '.' + parameter.getName();
final Properties cachedProperties = DOCUMENTATION_PROPERTIES_CACHE.getIfPresent(fileName);
if (cachedProperties != null) {
return cachedProperties.getProperty(propertyName);
final String propertyValue = cachedProperties.getProperty(propertyName);
return propertyValue != null ? DescriptionInfo.of(propertyValue) : null;
}
try (InputStream stream = AnnotatedServiceFactory.class.getClassLoader()
.getResourceAsStream(fileName)) {
Expand All @@ -610,7 +612,9 @@ static String findDescription(AnnotatedElement annotatedElement) {
final Properties properties = new Properties();
properties.load(stream);
DOCUMENTATION_PROPERTIES_CACHE.put(fileName, properties);
return properties.getProperty(propertyName);

final String propertyValue = properties.getProperty(propertyName);
return propertyValue != null ? DescriptionInfo.of(propertyValue) : null;
} catch (IOException exception) {
logger.warn("Failed to load an API description file: {}", fileName, exception);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
import com.linecorp.armeria.server.annotation.RequestConverterFunctionProvider;
import com.linecorp.armeria.server.annotation.RequestObject;
import com.linecorp.armeria.server.annotation.StringRequestConverterFunction;
import com.linecorp.armeria.server.docs.DescriptionInfo;

import io.netty.handler.codec.http.HttpConstants;
import scala.concurrent.ExecutionContext;
Expand Down Expand Up @@ -440,7 +441,7 @@ private static AnnotatedValueResolver of(AnnotatedElement annotatedElement,
requireNonNull(objectResolvers, "objectResolvers");
requireNonNull(dependencyInjector, "dependencyInjector");

final String description = findDescription(annotatedElement);
final DescriptionInfo description = findDescription(annotatedElement);
final Param param = annotatedElement.getAnnotation(Param.class);
if (param != null) {
final String name = findName(param, typeElement);
Expand Down Expand Up @@ -534,7 +535,7 @@ private static void warnOnDuplicateResolver(Executable constructorOrMethod,
private static AnnotatedValueResolver ofPathVariable(String name,
AnnotatedElement annotatedElement,
AnnotatedElement typeElement, Class<?> type,
@Nullable String description) {
@Nullable DescriptionInfo description) {
return new Builder(annotatedElement, type)
.annotationType(Param.class)
.httpElementName(name)
Expand All @@ -548,7 +549,7 @@ private static AnnotatedValueResolver ofPathVariable(String name,
private static AnnotatedValueResolver ofQueryParam(String name,
AnnotatedElement annotatedElement,
AnnotatedElement typeElement, Class<?> type,
@Nullable String description,
@Nullable DescriptionInfo description,
@Nullable String serviceQueryDelimiter) {
String queryDelimiter = serviceQueryDelimiter;
final Delimiter delimiter = annotatedElement.getAnnotation(Delimiter.class);
Expand All @@ -574,7 +575,7 @@ private static AnnotatedValueResolver ofQueryParam(String name,
private static AnnotatedValueResolver ofFileParam(String name,
AnnotatedElement annotatedElement,
AnnotatedElement typeElement, Class<?> type,
@Nullable String description) {
@Nullable DescriptionInfo description) {
return new Builder(annotatedElement, type)
.annotationType(Param.class)
.httpElementName(name)
Expand All @@ -589,7 +590,7 @@ private static AnnotatedValueResolver ofFileParam(String name,
private static AnnotatedValueResolver ofHeader(String name,
AnnotatedElement annotatedElement,
AnnotatedElement typeElement, Class<?> type,
@Nullable String description) {
@Nullable DescriptionInfo description) {
return new Builder(annotatedElement, type)
.annotationType(Header.class)
.httpElementName(name)
Expand All @@ -607,7 +608,7 @@ private static AnnotatedValueResolver ofRequestObject(AnnotatedElement annotated
Class<?> type, Set<String> pathParams,
List<RequestObjectResolver> objectResolvers,
DependencyInjector dependencyInjector,
@Nullable String description) {
@Nullable DescriptionInfo description) {
// To do recursive resolution like a bean inside another bean, the original object resolvers should
// be passed into the AnnotatedBeanFactoryRegistry#register.
final BeanFactoryId beanFactoryId = AnnotatedBeanFactoryRegistry.register(
Expand Down Expand Up @@ -892,7 +893,7 @@ private static Type parameterizedTypeOf(AnnotatedElement element) {
private final Object defaultValue;

@Nullable
private final String description;
private final DescriptionInfo description;

private final BiFunction<AnnotatedValueResolver, ResolverContext, Object> resolver;

Expand All @@ -911,7 +912,7 @@ private AnnotatedValueResolver(@Nullable Class<? extends Annotation> annotationT
@Nullable Class<?> containerType, Class<?> elementType,
@Nullable ParameterizedType parameterizedElementType,
@Nullable String defaultValue,
@Nullable String description,
@Nullable DescriptionInfo description,
BiFunction<AnnotatedValueResolver, ResolverContext, Object> resolver,
@Nullable BeanFactoryId beanFactoryId,
AggregationStrategy aggregationStrategy) {
Expand Down Expand Up @@ -987,7 +988,7 @@ Object defaultValue() {
}

@Nullable
String description() {
DescriptionInfo description() {
return description;
}

Expand Down Expand Up @@ -1067,7 +1068,7 @@ private static final class Builder {
private boolean supportContainer;
private boolean supportDefault;
@Nullable
private String description;
private DescriptionInfo description;
@Nullable
private BiFunction<AnnotatedValueResolver, ResolverContext, Object> resolver;
@Nullable
Expand Down Expand Up @@ -1135,7 +1136,7 @@ private Builder typeElement(AnnotatedElement typeElement) {
/**
* Sets the description of the {@link AnnotatedElement}.
*/
private Builder description(@Nullable String description) {
private Builder description(@Nullable DescriptionInfo description) {
this.description = description;
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import java.lang.annotation.Target;

import com.linecorp.armeria.internal.server.annotation.DefaultValues;
import com.linecorp.armeria.server.docs.DocService;
import com.linecorp.armeria.server.docs.Markup;

/**
* An annotation used in annotated HTTP service. This describes:
Expand All @@ -32,11 +34,16 @@
* </ul>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER })
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD })
public @interface Description {

/**
* The description of a type, a field, a method or a parameter.
*/
String value() default DefaultValues.UNSPECIFIED;

/**
* The supported markup type in {@link DocService}.
*/
Markup markup() default Markup.NONE;
}
Loading