Skip to content
This repository was archived by the owner on Dec 19, 2023. It is now read-only.

Commit 9d53ef9

Browse files
authored
Support for @JsonProperty.Access (#383)
* Support for @JsonProperty.Access * added test & improvements
1 parent 826130e commit 9d53ef9

14 files changed

+293
-66
lines changed

spring-auto-restdocs-core/src/main/java/capital/scalable/restdocs/jackson/FieldDocumentationArrayVisitor.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
*/
2020
package capital.scalable.restdocs.jackson;
2121

22+
import com.fasterxml.jackson.annotation.JsonProperty;
2223
import com.fasterxml.jackson.databind.JavaType;
2324
import com.fasterxml.jackson.databind.JsonMappingException;
2425
import com.fasterxml.jackson.databind.SerializerProvider;
@@ -33,23 +34,25 @@ class FieldDocumentationArrayVisitor extends JsonArrayFormatVisitor.Base {
3334
private final String path;
3435
private final TypeRegistry typeRegistry;
3536
private final TypeFactory typeFactory;
37+
private final JsonProperty.Access skipAccessor;
3638

3739
public FieldDocumentationArrayVisitor(SerializerProvider provider,
3840
FieldDocumentationVisitorContext context, String path, TypeRegistry typeRegistry,
39-
TypeFactory typeFactory) {
41+
TypeFactory typeFactory, JsonProperty.Access skipAccessor) {
4042
super(provider);
4143
this.context = context;
4244
this.path = path;
4345
this.typeRegistry = typeRegistry;
4446
this.typeFactory = typeFactory;
47+
this.skipAccessor = skipAccessor;
4548
}
4649

4750
@Override
4851
public void itemsFormat(JsonFormatVisitable handler, JavaType elementType)
4952
throws JsonMappingException {
5053
String elementPath = path + "[]";
5154
JsonFormatVisitorWrapper visitor = new FieldDocumentationVisitorWrapper(getProvider(),
52-
context, elementPath, null, typeRegistry, typeFactory);
55+
context, elementPath, null, typeRegistry, typeFactory, skipAccessor);
5356
handler.acceptJsonFormatVisitor(visitor, elementType);
5457
}
5558
}

spring-auto-restdocs-core/src/main/java/capital/scalable/restdocs/jackson/FieldDocumentationGenerator.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
* Licensed under the Apache License, Version 2.0 (the "License");
88
* you may not use this file except in compliance with the License.
99
* You may obtain a copy of the License at
10-
*
10+
*
1111
* http://www.apache.org/licenses/LICENSE-2.0
12-
*
12+
*
1313
* Unless required by applicable law or agreed to in writing, software
1414
* distributed under the License is distributed on an "AS IS" BASIS,
1515
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -31,6 +31,7 @@
3131
import capital.scalable.restdocs.constraints.ConstraintReader;
3232
import capital.scalable.restdocs.i18n.SnippetTranslationResolver;
3333
import capital.scalable.restdocs.javadoc.JavadocReader;
34+
import com.fasterxml.jackson.annotation.JsonProperty;
3435
import com.fasterxml.jackson.databind.DeserializationConfig;
3536
import com.fasterxml.jackson.databind.JavaType;
3637
import com.fasterxml.jackson.databind.JsonMappingException;
@@ -52,18 +53,24 @@ public class FieldDocumentationGenerator {
5253
private final ConstraintReader constraintReader;
5354
private final TypeMapping typeMapping;
5455
private final SnippetTranslationResolver translationResolver;
56+
private final JsonProperty.Access skipAccessor;
5557

56-
public FieldDocumentationGenerator(ObjectWriter writer,
57-
DeserializationConfig deserializationConfig,
58-
JavadocReader javadocReader,
59-
ConstraintReader constraintReader,
60-
TypeMapping typeMapping, SnippetTranslationResolver translationResolver) {
58+
public FieldDocumentationGenerator(
59+
ObjectWriter writer,
60+
DeserializationConfig deserializationConfig,
61+
JavadocReader javadocReader,
62+
ConstraintReader constraintReader,
63+
TypeMapping typeMapping,
64+
SnippetTranslationResolver translationResolver,
65+
JsonProperty.Access skipAccessor
66+
) {
6167
this.writer = writer;
6268
this.deserializationConfig = deserializationConfig;
6369
this.javadocReader = javadocReader;
6470
this.constraintReader = constraintReader;
6571
this.typeMapping = typeMapping;
6672
this.translationResolver = translationResolver;
73+
this.skipAccessor = skipAccessor;
6774
}
6875

6976
public FieldDescriptors generateDocumentation(Type baseType, TypeFactory typeFactory)
@@ -74,7 +81,7 @@ public FieldDescriptors generateDocumentation(Type baseType, TypeFactory typeFac
7481

7582
FieldDocumentationVisitorWrapper visitorWrapper = FieldDocumentationVisitorWrapper.create(
7683
javadocReader, constraintReader, deserializationConfig,
77-
new TypeRegistry(typeMapping, types), typeFactory, translationResolver);
84+
new TypeRegistry(typeMapping, types), typeFactory, translationResolver, skipAccessor);
7885

7986
for (JavaType type : types) {
8087
log.debug("(TOP) {}", type.getRawClass().getSimpleName());

spring-auto-restdocs-core/src/main/java/capital/scalable/restdocs/jackson/FieldDocumentationObjectVisitor.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* #%L
33
* Spring Auto REST Docs Core
44
* %%
5-
* Copyright (C) 2015 - 2019 Scalable Capital GmbH
5+
* Copyright (C) 2015 - 2020 Scalable Capital GmbH
66
* %%
77
* Licensed under the Apache License, Version 2.0 (the "License");
88
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
2626
import java.util.HashSet;
2727
import java.util.Set;
2828

29+
import com.fasterxml.jackson.annotation.JsonProperty;
2930
import com.fasterxml.jackson.databind.BeanProperty;
3031
import com.fasterxml.jackson.databind.JavaType;
3132
import com.fasterxml.jackson.databind.JsonMappingException;
@@ -54,15 +55,23 @@ class FieldDocumentationObjectVisitor extends JsonObjectFormatVisitor.Base {
5455
private final String path;
5556
private final TypeRegistry typeRegistry;
5657
private final TypeFactory typeFactory;
58+
private final JsonProperty.Access skipAccessor;
5759

58-
public FieldDocumentationObjectVisitor(SerializerProvider provider,
59-
FieldDocumentationVisitorContext context, String path, TypeRegistry typeRegistry,
60-
TypeFactory typeFactory) {
60+
public FieldDocumentationObjectVisitor(
61+
SerializerProvider provider,
62+
FieldDocumentationVisitorContext context,
63+
String path,
64+
TypeRegistry typeRegistry,
65+
TypeFactory typeFactory,
66+
JsonProperty.Access skipAccessor
67+
68+
) {
6169
super(provider);
6270
this.context = context;
6371
this.path = path;
6472
this.typeRegistry = typeRegistry;
6573
this.typeFactory = typeFactory;
74+
this.skipAccessor = skipAccessor;
6675
}
6776

6877
/**
@@ -84,6 +93,9 @@ public void optionalProperty(BeanProperty prop) throws JsonMappingException {
8493
}
8594

8695
public void property(BeanProperty prop, boolean required) throws JsonMappingException {
96+
if (skipProperty(prop))
97+
return;
98+
8799
String jsonName = prop.getName();
88100
String fieldName = prop.getMember().getName();
89101

@@ -100,11 +112,15 @@ public void property(BeanProperty prop, boolean required) throws JsonMappingExce
100112
return;
101113
}
102114

103-
104115
visitType(prop, jsonName, fieldName, javaType, ser, required);
105116
}
106117
}
107118

119+
private boolean skipProperty(BeanProperty prop) {
120+
JsonProperty jsonProperty = prop.getMember().getAnnotation(JsonProperty.class);
121+
return jsonProperty != null && skipAccessor == jsonProperty.access();
122+
}
123+
108124
private void visitType(BeanProperty prop, String jsonName, String fieldName, JavaType fieldType,
109125
JsonSerializer<?> ser, boolean required) throws JsonMappingException {
110126
String fieldPath = path + (path.isEmpty() ? "" : ".") + jsonName;
@@ -116,7 +132,7 @@ private void visitType(BeanProperty prop, String jsonName, String fieldName, Jav
116132
fieldPath, shouldExpand, required);
117133

118134
JsonFormatVisitorWrapper visitor = new FieldDocumentationVisitorWrapper(getProvider(),
119-
context, fieldPath, fieldInfo, typeRegistry, typeFactory);
135+
context, fieldPath, fieldInfo, typeRegistry, typeFactory, skipAccessor);
120136

121137
ser.acceptJsonFormatVisitor(visitor, fieldType);
122138
}

spring-auto-restdocs-core/src/main/java/capital/scalable/restdocs/jackson/FieldDocumentationVisitorWrapper.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
* Licensed under the Apache License, Version 2.0 (the "License");
88
* you may not use this file except in compliance with the License.
99
* You may obtain a copy of the License at
10-
*
10+
*
1111
* http://www.apache.org/licenses/LICENSE-2.0
12-
*
12+
*
1313
* Unless required by applicable law or agreed to in writing, software
1414
* distributed under the License is distributed on an "AS IS" BASIS,
1515
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -28,6 +28,7 @@
2828
import capital.scalable.restdocs.constraints.ConstraintReader;
2929
import capital.scalable.restdocs.i18n.SnippetTranslationResolver;
3030
import capital.scalable.restdocs.javadoc.JavadocReader;
31+
import com.fasterxml.jackson.annotation.JsonProperty;
3132
import com.fasterxml.jackson.databind.DeserializationConfig;
3233
import com.fasterxml.jackson.databind.JavaType;
3334
import com.fasterxml.jackson.databind.JsonMappingException;
@@ -55,29 +56,33 @@ class FieldDocumentationVisitorWrapper implements JsonFormatVisitorWrapper {
5556
private final InternalFieldInfo fieldInfo;
5657
private final TypeRegistry typeRegistry;
5758
private final TypeFactory typeFactory;
59+
private final JsonProperty.Access skipAccessor;
5860

5961
FieldDocumentationVisitorWrapper(FieldDocumentationVisitorContext context, String path,
60-
InternalFieldInfo fieldInfo, TypeRegistry typeRegistry, TypeFactory typeFactory) {
61-
this(null, context, path, fieldInfo, typeRegistry, typeFactory);
62+
InternalFieldInfo fieldInfo, TypeRegistry typeRegistry, TypeFactory typeFactory,
63+
JsonProperty.Access skipAccessor) {
64+
this(null, context, path, fieldInfo, typeRegistry, typeFactory, skipAccessor);
6265
}
6366

6467
FieldDocumentationVisitorWrapper(SerializerProvider provider,
6568
FieldDocumentationVisitorContext context, String path, InternalFieldInfo fieldInfo,
66-
TypeRegistry typeRegistry, TypeFactory typeFactory) {
69+
TypeRegistry typeRegistry, TypeFactory typeFactory, JsonProperty.Access skipAccessor) {
6770
this.provider = provider;
6871
this.context = context;
6972
this.path = path;
7073
this.fieldInfo = fieldInfo;
7174
this.typeRegistry = typeRegistry;
7275
this.typeFactory = typeFactory;
76+
this.skipAccessor = skipAccessor;
7377
}
7478

7579
public static FieldDocumentationVisitorWrapper create(JavadocReader javadocReader,
76-
ConstraintReader constraintReader, DeserializationConfig deserializationConfig,
77-
TypeRegistry typeRegistry, TypeFactory typeFactory, SnippetTranslationResolver translationResolver) {
80+
ConstraintReader constraintReader, DeserializationConfig deserializationConfig,
81+
TypeRegistry typeRegistry, TypeFactory typeFactory, SnippetTranslationResolver translationResolver,
82+
JsonProperty.Access skipAccessor) {
7883
FieldDocumentationVisitorContext context = new FieldDocumentationVisitorContext(
7984
javadocReader, constraintReader, deserializationConfig, translationResolver);
80-
return new FieldDocumentationVisitorWrapper(context, "", null, typeRegistry, typeFactory);
85+
return new FieldDocumentationVisitorWrapper(context, "", null, typeRegistry, typeFactory, skipAccessor);
8186
}
8287

8388
@Override
@@ -96,7 +101,7 @@ public JsonObjectFormatVisitor expectObjectFormat(JavaType type) throws JsonMapp
96101
if (shouldExpand() && (topLevelPath() || !wasVisited(type))) {
97102
log.trace("({}) {} expanding", path, toString(type));
98103
return new FieldDocumentationObjectVisitor(provider, context, path,
99-
withVisitedType(type), typeFactory);
104+
withVisitedType(type), typeFactory, skipAccessor);
100105
} else {
101106
log.trace("({}) {} NOT expanding", path, toString(type));
102107
return new JsonObjectFormatVisitor.Base();
@@ -113,7 +118,7 @@ public JsonArrayFormatVisitor expectArrayFormat(JavaType arrayType)
113118
// do not add this type to visited now, it will be done in expectObjectFormat for
114119
// content type of this array
115120
return new FieldDocumentationArrayVisitor(provider, context, path,
116-
typeRegistry, typeFactory);
121+
typeRegistry, typeFactory, skipAccessor);
117122
} else {
118123
log.trace("({}) {} NOT expanding array", path, "<unknown>");
119124
return new JsonArrayFormatVisitor.Base();

spring-auto-restdocs-core/src/main/java/capital/scalable/restdocs/jackson/JacksonResultHandlers.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ private static class JacksonPreparingResultHandler implements ResultHandler {
7979
private final ConstraintDescriptionResolver constraintDescriptionResolver;
8080

8181
public JacksonPreparingResultHandler(ObjectMapper objectMapper, TypeMapping typeMapping, SnippetTranslationResolver translationResolver, ConstraintDescriptionResolver constraintDescriptionResolver) {
82-
this.objectMapper = objectMapper;
82+
this.objectMapper = new SardObjectMapper(objectMapper);
8383
this.typeMapping = typeMapping;
8484
this.translationResolver = translationResolver;
8585
this.constraintDescriptionResolver = constraintDescriptionResolver;
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*-
2+
* #%L
3+
* Spring Auto REST Docs Core
4+
* %%
5+
* Copyright (C) 2015 - 2020 Scalable Capital GmbH
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package capital.scalable.restdocs.jackson;
21+
22+
import java.util.Map;
23+
24+
import com.fasterxml.jackson.annotation.JsonProperty;
25+
import com.fasterxml.jackson.databind.JavaType;
26+
import com.fasterxml.jackson.databind.ObjectMapper;
27+
import com.fasterxml.jackson.databind.cfg.MapperConfig;
28+
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
29+
import com.fasterxml.jackson.databind.introspect.BasicClassIntrospector;
30+
import com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector;
31+
import com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder;
32+
33+
/**
34+
* Overrides class inspector so that we keep properties othewise filtered out via {@link JsonProperty#access()} in
35+
* {@link POJOPropertiesCollector#_removeUnwantedAccessor(java.util.Map)}.
36+
* The actual filtering happens now in
37+
* {@link FieldDocumentationObjectVisitor#skipProperty(com.fasterxml.jackson.databind.BeanProperty)}
38+
*/
39+
public class SardObjectMapper extends ObjectMapper {
40+
public SardObjectMapper(ObjectMapper objectMapper) {
41+
super(objectMapper);
42+
this.setConfig(getSerializationConfig().with(new SardClassIntrospector()));
43+
}
44+
45+
public static class SardClassIntrospector extends BasicClassIntrospector {
46+
@Override
47+
protected POJOPropertiesCollector constructPropertyCollector(MapperConfig<?> config, AnnotatedClass ac,
48+
JavaType type, boolean forSerialization, String mutatorPrefix) {
49+
return new SardPOJOPropertiesCollector(config, forSerialization, type, ac, mutatorPrefix);
50+
}
51+
}
52+
53+
public static class SardPOJOPropertiesCollector extends POJOPropertiesCollector {
54+
55+
protected SardPOJOPropertiesCollector(MapperConfig<?> config, boolean forSerialization,
56+
JavaType type, AnnotatedClass classDef, String mutatorPrefix) {
57+
super(config, forSerialization, type, classDef, mutatorPrefix);
58+
}
59+
60+
@Override
61+
protected void _removeUnwantedAccessor(Map<String, POJOPropertyBuilder> props) {
62+
// keep everything
63+
}
64+
}
65+
}

spring-auto-restdocs-core/src/main/java/capital/scalable/restdocs/payload/AbstractJacksonFieldSnippet.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import capital.scalable.restdocs.javadoc.JavadocReader;
3838
import capital.scalable.restdocs.section.SectionSupport;
3939
import capital.scalable.restdocs.snippet.StandardTableSnippet;
40+
import com.fasterxml.jackson.annotation.JsonProperty;
4041
import com.fasterxml.jackson.databind.JsonMappingException;
4142
import com.fasterxml.jackson.databind.ObjectMapper;
4243
import org.apache.commons.lang3.StringUtils;
@@ -69,18 +70,22 @@ protected FieldDescriptors createFieldDescriptors(Operation operation,
6970
ConstraintReader constraintReader = getConstraintReader(operation);
7071
SnippetTranslationResolver translationResolver = getTranslationResolver(operation);
7172
TypeMapping typeMapping = getTypeMapping(operation);
73+
JsonProperty.Access skipAcessor = getSkipAcessor();
7274

7375
Type type = getType(handlerMethod);
7476
if (type == null) {
7577
return new FieldDescriptors();
7678
}
7779

7880
try {
79-
FieldDescriptors fieldDescriptors = resolveFieldDescriptors(type, objectMapper,
80-
javadocReader, constraintReader, typeMapping, translationResolver);
81+
FieldDocumentationGenerator generator = new FieldDocumentationGenerator(
82+
objectMapper.writer(), objectMapper.getDeserializationConfig(), javadocReader,
83+
constraintReader, typeMapping, translationResolver, skipAcessor);
84+
FieldDescriptors fieldDescriptors = generator.generateDocumentation(type, objectMapper.getTypeFactory());
8185

8286
if (shouldFailOnUndocumentedFields()) {
83-
assertAllDocumented(fieldDescriptors.values(), translationResolver.translate(getHeaderKey(operation)).toLowerCase());
87+
assertAllDocumented(fieldDescriptors.values(),
88+
translationResolver.translate(getHeaderKey(operation)).toLowerCase());
8489
}
8590
return fieldDescriptors;
8691
} catch (JsonMappingException e) {
@@ -92,20 +97,15 @@ protected FieldDescriptors createFieldDescriptors(Operation operation,
9297

9398
protected abstract boolean shouldFailOnUndocumentedFields();
9499

100+
protected JsonProperty.Access getSkipAcessor() {
101+
return null;
102+
}
103+
95104
protected boolean isCollection(Class<?> type) {
96105
return Collection.class.isAssignableFrom(type) || Stream.class.isAssignableFrom(type) ||
97106
(SCALA_TRAVERSABLE != null && SCALA_TRAVERSABLE.isAssignableFrom(type));
98107
}
99108

100-
private FieldDescriptors resolveFieldDescriptors(Type type, ObjectMapper objectMapper,
101-
JavadocReader javadocReader, ConstraintReader constraintReader,
102-
TypeMapping typeMapping, SnippetTranslationResolver translationResolver) throws JsonMappingException {
103-
FieldDocumentationGenerator generator = new FieldDocumentationGenerator(
104-
objectMapper.writer(), objectMapper.getDeserializationConfig(), javadocReader,
105-
constraintReader, typeMapping, translationResolver);
106-
return generator.generateDocumentation(type, objectMapper.getTypeFactory());
107-
}
108-
109109
@Override
110110
public String getFileName() {
111111
return getSnippetName();

0 commit comments

Comments
 (0)