RestDocs does double documentation on request parameters - spring-boot

Using RestDocs version 1.1.1.RELEASE and SpringBoot 1.4.0.RELEASE, parameters when using "httpRequest" is documented double.
The following code:
#RunWith(SpringRunner.class)
#WebMvcTest(ProductResource.class)
#AutoConfigureRestDocs("target/generated-snippets")
public class ProductResourceDocumentation {
private static final String PROPERTY_ID_VALUE = "d7612";
private static final String SALES_MARKET_VALUE = "999";
private static final String SEASON_VALUE = "2016";
private static final String SECTIONS_VALUE = "buildings";
private static final String SHOW_DESCRIPTIONS_VALUE = "false";
private static final String STRING = "string";
private static final Integer NUMBER = 1;
private static final String NOT_FOUND = "404 Not Found";
private static final String BAD_REQUEST = "400 Bad Request";
private RestDocumentationResultHandler documentationResultHandler;
#MockBean
private ProductHandler productHandler;
#Autowired
private MockMvc mockMvc;
#Before
public void setup() {
this.documentationResultHandler = document("{class-name}/{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()));
}
#Test
public void listProducts() throws Exception {
Product product = getBasicProductsRepresentation();
when(productHandler.getProduct(PROPERTY_ID_VALUE, SALES_MARKET_VALUE, SEASON_VALUE, null, null)).thenReturn(product);
mockMvc.perform(RestDocumentationRequestBuilders.get("/products/{propertyId}", PROPERTY_ID_VALUE)
.param("salesMarket", SALES_MARKET_VALUE)
.param("season", SEASON_VALUE))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.propertyID", is(PROPERTY_ID_VALUE)))
.andExpect(jsonPath("$.season", is(SEASON_VALUE)))
.andDo(documentationResultHandler.document(
pathParameters(
parameterWithName("propertyId").description(PATH_PARAM__PROPERTY_ID)
),
requestParameters(
parameterWithName("salesMarket").description(REQUEST_PARAM__SALES_MARKET),
parameterWithName("season").description(REQUEST_PARAM__SEASON)
),
httpRequest(), httpResponse()
));
}
Produces the following documentation ("salesMarket=999&season=2016" is documented twice):
GET /products/d7612? salesMarket=999&season=2016&salesMarket=999&season=2016 HTTP/1.1
Have anybody experienced anything similar or know what the problem is?

It's a bug that's been fixed in 1.1.2.RELEASE. Upgrading will resolve the problem. When upgrading make sure that you get 1.1.2 of both spring-restdocs-mockmvc and spring-restdocs-core. The latter module is where the fix is.

We get "double documentation" when we set a query param (see block below) to an empty string (""):
GET /Thing/OtherThing/Foo/?thinger=FOO&block=&bla=5&block= HTTP/1.1
We use Spring Boot 1.5.2 and Spring REST Docs 1.1.2.RELEASE.

Related

java.lang.IllegalArgumentException: column '`kitType`' does not exist. Available columns: [..., dependencies, ൔ, DMLoader, ...]

I get an expcetion when I try query some data from db.(RoomDb Version used:2.2.5)
It never reproduced in DEV. It only happened in PRD sometime on API 27 28 29 30.
Crash scenes:
1. Some time columnName of my database was changed to garbled.
java.lang.IllegalArgumentException: column 'kitType' does not exist. Available columns: [kitName, apiLevel, pkgVersionCode, packageName, kitCodePath, moduleCodePath, soLibraryPath, dependencies, ൔ, DMLoader, archType, forceUpgrade, ignoreForceUpgradeTime, mainEntry, kitState, moduleState, usesLibraryPath, appClass, reservedString1, reservedString2, reservedInt1, reservedInt2, reservedInt3]
as show above:kitType was changed to ൔ.
2. Some time columnName of my database was changed to other columnName.
java.lang.IllegalArgumentException: column 'reservedInt2' does not exist. Available columns: [id, packageName, pkgVersionCode, classname, actions, categories, intentFilter, reservedString1, reservedString2, reservedInt1, reservedInp2, reservedInt3, packageState]
as show above: reservedInt2 was changed to reservedInp2.
This is my Entity code:
#Entity(tableName = PackageInfoEntity.PACKAGE_INFO_TABLE_NAME,
primaryKeys = {PackageInfoEntity.PACKAGE_NAME, PackageInfoEntity.PKG_VERSION_CODE})
public class PackageInfoEntity {
#Ignore
public static final String PACKAGE_INFO_TABLE_NAME = "PackageInfoTable";
#Ignore
public static final String PACKAGE_NAME = "packageName";
#Ignore
public static final String PKG_VERSION_CODE = "pkgVersionCode";
#Ignore
public static final String APPLICATION_INFO = "applicationInfo";
#Ignore
public static final String PACKAGE_INFO = "packageInfo";
#Ignore
public static final String REF_COUNT = "refCount";
#Ignore
public static final String LAST_MODIFY = "lastModify";
#Ignore
public static final String PACKAGE_STATE = "packageState";
#Ignore
public static final String MODULE_PKG_STATE = "modulePkgState";
#Ignore
public static final String INVALID_TIME = "invalidTime";
#Ignore
public static final String PRE_PKG_NAME = "prePkgNames";
#Ignore
public static final String FUTURE_PKG_NAME = "futurePkgName";
#Ignore
private static final String TAG = "PackageInfoEntity";
#Ignore
public static final String RESERVEDSTRING_01 = "reservedString1";
#Ignore
public static final String RESERVEDSTRING_02 = "reservedString2";
#Ignore
public static final String RESERVEDINT_01 = "reservedInt1";
#Ignore
public static final String RESERVEDINT_02 = "reservedInt2";
#Ignore
public static final String RESERVEDINT_03 = "reservedInt3";
#NonNull
public String packageName;
#NonNull
public int pkgVersionCode;
#NonNull
#ColumnInfo(typeAffinity = ColumnInfo.BLOB)
public ApplicationInfo applicationInfo;
#NonNull
#ColumnInfo(typeAffinity = ColumnInfo.BLOB)
public PackageInfo packageInfo;
public int packageState;
public int modulePkgState;
public long invalidTime;
public String prePkgNames;
public String futurePkgName;
public String reservedString1;
public String reservedString2;
public Integer reservedInt1;
public Integer reservedInt2;
public Integer reservedInt3;
}
What I have tried:
This error never happened on my In-progress version with same app version.
I want to try add a try/catch block,but this database query is located on so many functions.
So is any advice can I figure this out.

Why methods doesn't see values set in constructor? (Spring Boot)

I have a class AuthorizationService like this:
#Service
public class AuthorizationService {
private final String code;
private final String client_id;
private final String redirect_uri;
private final String scope;
private final String show_dialog;
#Autowired
public AuthorizationService(
#Value("${spotify-property.response_type}") String code,
#Value("${spotify-property.client_id}") String client_id,
#Value("${spotify-property.redirect_uri}") String redirect_uri,
#Value("${spotify-property.scope}") String scope,
#Value("${spotify-property.redirect_uri}") String show_dialog) {
this.code = code;
this.client_id = client_id;
this.redirect_uri = redirect_uri;
this.scope = scope;
this.show_dialog = show_dialog;
System.out.println(code); //works
}
public ResponseEntity<HttpHeaders> getSpotifyRedirectionCode() {
HttpHeaders headers = new HttpHeaders();
System.out.println(client_id); //doesn't work
System.out.println(this.code); //does not work either
System.out.println(redirect_uri);
System.out.println(scope);
System.out.println(show_dialog);
//more code doesn't matter
return new ResponseEntity<>(headers, HttpStatus.TEMPORARY_REDIRECT);
}
Why these variables are ok in constructor but in methods are null, how to set them in correct way? Also
#Value("${spotify-property.response_type}")
private final String code;
does not work either.
Your class fields are final so get instantiated without your configuration values.
You can either use the constructor as you have before, or remove the final from your fields.
I'm not sure if my answer is 100% correct. But I believe that you cannot change final variables. The right way would be to remove the final keyword and keep the #Value annotations on the fields and the you don't have to use it in the constructor.
#Service
#RequiredArgsConstructor
public class AuthorizationService {
#Value("${spotify-property.response_type}")
private String code;
#Value("${spotify-property.client_id}")
private String client_id;
#Value("${spotify-property.redirect_uri}")
private String redirect_uri;
#Value("${spotify-property.scope}")
private String scope;
#Value("${spotify-property.redirect_uri}")
private String show_dialog;
public ResponseEntity<HttpHeaders> getSpotifyRedirectionCode() {
HttpHeaders headers = new HttpHeaders();
System.out.println(client_id); //works
System.out.println(this.code); //works too
System.out.println(this.redirect_uri);
System.out.println(this.scope);
System.out.println(this.show_dialog);
//more code doesn't matter
return new ResponseEntity<>(headers, HttpStatus.TEMPORARY_REDIRECT);
} }

unable to perform Delete operation on Odata services using s4sdk

I followed the blog & I was able to perform create, read & update operations on my custom OData service, but I am unable to find any blog/document for delete operation.
Please help.
There is no dedicated blog post for executing delete operations on custom OData Services but we would advise you to follow this pattern:
public class DeleteAddressCommand extends ErpCommand<Integer> {
private static final Logger logger = CloudLoggerFactory.getLogger(DeleteAddressCommand.class);
private final BusinessPartnerService service;
private final String businessPartnerId;
private final String addressId;
public DeleteAddressCommand(final BusinessPartnerService service,
final String businessPartnerId, final String addressId) {
super(HystrixUtil.getDefaultErpCommandSetter(
DeleteAddressCommand.class,
HystrixUtil.getDefaultErpCommandProperties().withExecutionTimeoutInMilliseconds(10000)));
this.service = service;
this.businessPartnerId = businessPartnerId;
this.addressId = addressId;
}
#Override
protected Integer run() throws Exception {
final BusinessPartnerAddress addressToDelete = BusinessPartnerAddress.builder()
.businessPartner(businessPartnerId)
.addressID(addressId)
.build();
final ODataDeleteResult oDataDeleteResult = service
.deleteBusinessPartnerAddress(addressToDelete)
.execute();
return oDataDeleteResult.getHttpStatusCode();
}
}
I pasted the code from this official example
Best wishes
Florian

Correct usage of NonEmptyStringParam in Dropwizard

I'm a newbee using Dropwizard and I'm following the tutorial in the website to create the hello world application. Can anyone please explain to me how to use NonEmptyStringParam to print something like "Hello, stranger!" if no parameter is provided to sayHello?
The following is my Resource code and it outputs:
{"id":1,"content":"Hello, Optional[Stranger]!"}
instead of
{"id":1,"content":"Hello, Stranger!"}
public class HelloWorldResource {
private final String template;
private final NonEmptyStringParam defaultName;
private final AtomicLong counter;
public HelloWorldResource(String template, String defaultName) {
this.template = template;
this.defaultName = new NonEmptyStringParam(defaultName);
this.counter = new AtomicLong();
}
#GET
#Timed
public Saying sayHello(#QueryParam("name") Optional<NonEmptyStringParam> name) {
final String value = String.format(template, name.orElse(defaultName));
return new Saying(counter.incrementAndGet(), value);
}
}
Thanks!
You shouldn't wrap the NonEmptyStringParam with Optional<>. See the testcase from the Dropwizard source.
Also remove the wrap of defaultName with NonEmptyStringParam in the constructor method.

Couchbase 5 bucket password setting

I am trying to write a sample in order to learn couchbase. I am trying to use it with spring boot and it’s crud repositories .
So I have downloaded latest docker image but the point is: i could not find the password of the bucket. The couchbase console allows only user creation but in spring, there is no equivalent of this usage like a username/password. It allows only bucketName and password which does not seem compatible with couchbase 5.
Am I missing anything here or is spring not compatible with couchbase 5? If spring is not compatible, which version of couchbase is ok?
Thx
Spring Data Couchbase is compatible with Couchbase Server 5.0. You can achieve the same auth as 4.x by creating a user with the same name as the bucket, then just use that bucket name and password from Spring Data if it's prior to 3.0/Kay.
The docs should cover this and if there's anything confusing there, please click the "feedback" button and offer what could be improved!
https://developer.couchbase.com/documentation/server/5.0/security/security-authorization.html
https://developer.couchbase.com/documentation/server/5.0/security/concepts-rba-for-apps.html
https://developer.couchbase.com/documentation/server/5.0/security/security-resources-under-access-control.html
I faced the same issue. I started debugging by getting into AbstractCouchbaseConfiguration and there i found
public abstract class AbstractCouchbaseConfiguration
extends AbstractCouchbaseDataConfiguration implements CouchbaseConfigurer {
....//some other configuration
#Override
#Bean(name = BeanNames.COUCHBASE_CLUSTER_INFO)
public ClusterInfo couchbaseClusterInfo() throws Exception {
return couchbaseCluster().clusterManager(getBucketName(), getBucketPassword()).info();
}
What i did is created a bucket with the same name as of my couchbase user.
couchbase username : userdetail
couchbase password : ******
bucket name : userdetail
Couchbase driver supports connection to Couchbase 5 buckets using username/password. Problem is that spring-data-couchbase is not developed fast enough to cover all the new features Couchbase introduces. So, we need to help Spring to use a new bucket connection, doing it by overriding Couchbase cluster instantiation method of spring-data-couchbase configuration base class - org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration. This is the method we are looking at :
#Override
#Bean(name = BeanNames.COUCHBASE_CLUSTER_INFO)
public ClusterInfo couchbaseClusterInfo() throws Exception {
return couchbaseCluster().clusterManager(getBucketName(), getBucketPassword()).info();
}
as we can see, it's not using username, just bucket and password, so in our configuration we will override it as following :
#Override
#Bean(name = BeanNames.COUCHBASE_CLUSTER_INFO)
public ClusterInfo couchbaseClusterInfo() throws Exception {
return couchbaseCluster().authenticate(couchbaseUsername, couchbasePassword).clusterManager().info();
}
that's it. Here is the full code of my spring-data-couchbase configuration :
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.cluster.ClusterInfo;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
import org.springframework.data.couchbase.config.BeanNames;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
import javax.inject.Inject;
import java.security.KeyStore;
import java.util.List;
/**
* #author by avoinovan
*/
#Configuration
#EnableCouchbaseRepositories
public class ModelConfig extends AbstractCouchbaseConfiguration {
private final static int DEFAULT_HTTP_PORT = 8091;
private final static int DEFAULT_HTTP_SSL_PORT = 18091;
private final static int DEFAULT_CARRIER_PORT = 11210;
private final static int DEFAULT_CARRIER_SSL_PORT = 11207;
private final static long DEFAULT_KEEP_ALIVE_INTERVAL = 30000;
private final static int DEFAULT_SOCKET_CONNECT_TIMEOUT_MS = 5000;
private final static long DEFAULT_CONNECT_TIMEOUT_MS = 5000;
private final static long DEFAULT_MANAGEMENT_TIMEOUT_MS = 75000;
private final static long DEFAULT_DISCONNECT_TIMEOUT_MS = 25000;
private final static String PROPERTY_KEEP_ALIVE_INTERVAL_MS = "couchbase.keep_alive_interval_ms";
private final static String PROPERTY_SOCKET_CONNECT_TIMEOUT_MS = "couchbase.socket_connect_timeout_ms";
private final static String PROPERTY_CONNECT_TIMEOUT_MS = "couchbase.connect_timeout_ms";
private final static String PROPERTY_MANAGEMENT_TIMEOUT_MS = "couchbase.management_timeout_ms";
private final static String PROPERTY_DISCONNECT_TIMEOUT_MS = "couchbase.disconnect_timeout_ms";
private final static String PROPERTY_SSL_ENABLED = "couchbase.ssl.enabled";
private final static String PROPERTY_SSL_KEYSTORE_FILE = "couchbase.ssl.keystore.file";
private final static String PROPERTY_SSL_KEYSTORE_PASSWORD = "couchbase.ssl.keystore.password";
private final static String PROPERTY_SSL_TRUSTSTORE_FILE = "couchbase.ssl.truststore.file";
private final static String PROPERTY_SSL_TRUSTSTORE_PASSWORD = "couchbase.ssl.truststore.password";
private final static String PROPERTY_BOOTSTRAP_HTTP_ENABLED = "couchbase.bootstrap.http.enabled";
private final static String PROPERTY_BOOTSTRAP_HTTP_PORT = "couchbase.bootstrap.http.port";
private final static String PROPERTY_BOOTSTRAP_HTTP_SSL_PORT = "couchbase.bootstrap.http.ssl.port";
private final static String PROPERTY_BOOTSTRAP_CARRIER_ENABLED = "couchbase.bootstrap.carrier.enabled";
private final static String PROPERTY_BOOTSTRAP_CARRIER_PORT = "couchbase.bootstrap.carrier.port";
private final static String PROPERTY_BOOTSTRAP_CARRIER_SSL_PORT = "couchbase.bootstrap.carrier.ssl.port";
#Value("#{'${spring.couchbase.bootstrap-hosts}'.split(',')}")
private List<String> couchbaseBootstrapHosts;
#Value("${spring.couchbase.bucket.name}")
private String bucketName;
#Value("${spring.couchbase.password}")
private String couchbasePassword;
#Value("${spring.couchbase.username}")
private String couchbaseUsername;
private final Environment environment;
private final ResourceLoader resourceLoader;
#Inject
public ModelConfig(final Environment environment,
final ResourceLoader resourceLoader) {
this.environment = environment;
this.resourceLoader = resourceLoader;
}
protected List<String> getBootstrapHosts() {
return couchbaseBootstrapHosts;
}
protected String getBucketName() {
return bucketName;
}
protected String getBucketPassword() {
return couchbasePassword;
}
protected CouchbaseEnvironment getEnvironment() {
return DefaultCouchbaseEnvironment.builder()
.keepAliveInterval(environment.getProperty(PROPERTY_KEEP_ALIVE_INTERVAL_MS,
Long.class,
DEFAULT_KEEP_ALIVE_INTERVAL))
// timeout settings
.socketConnectTimeout(environment.getProperty(PROPERTY_SOCKET_CONNECT_TIMEOUT_MS,
Integer.class,
DEFAULT_SOCKET_CONNECT_TIMEOUT_MS))
.connectTimeout(environment.getProperty(PROPERTY_CONNECT_TIMEOUT_MS,
Long.class,
DEFAULT_CONNECT_TIMEOUT_MS))
.managementTimeout(environment.getProperty(PROPERTY_MANAGEMENT_TIMEOUT_MS,
Long.class,
DEFAULT_MANAGEMENT_TIMEOUT_MS))
.disconnectTimeout(environment.getProperty(PROPERTY_DISCONNECT_TIMEOUT_MS,
Long.class,
DEFAULT_DISCONNECT_TIMEOUT_MS))
// port and ssl
.sslEnabled(environment.getProperty(PROPERTY_SSL_ENABLED, Boolean.class, false))
.bootstrapHttpEnabled(environment.getProperty(PROPERTY_BOOTSTRAP_HTTP_ENABLED,
Boolean.class,
Boolean.TRUE))
.bootstrapHttpDirectPort(environment.getProperty(PROPERTY_BOOTSTRAP_HTTP_PORT,
Integer.class,
DEFAULT_HTTP_PORT))
.bootstrapHttpSslPort(environment.getProperty(PROPERTY_BOOTSTRAP_HTTP_SSL_PORT,
Integer.class,
DEFAULT_HTTP_SSL_PORT))
.bootstrapCarrierEnabled(environment.getProperty(PROPERTY_BOOTSTRAP_CARRIER_ENABLED,
Boolean.class,
Boolean.TRUE))
.bootstrapCarrierDirectPort(environment.getProperty(PROPERTY_BOOTSTRAP_CARRIER_PORT,
Integer.class,
DEFAULT_CARRIER_PORT))
.bootstrapCarrierSslPort(environment.getProperty(PROPERTY_BOOTSTRAP_CARRIER_SSL_PORT,
Integer.class,
DEFAULT_CARRIER_SSL_PORT))
// keystore and trust store
.sslKeystore(createKeyStore(environment, resourceLoader))
.sslTruststore(createTrustStore(environment, resourceLoader))
.build();
}
#Override
#Bean(name = BeanNames.COUCHBASE_CLUSTER_INFO)
public ClusterInfo couchbaseClusterInfo() throws Exception {
return couchbaseCluster().authenticate(couchbaseUsername, couchbasePassword).clusterManager().info();
}
/**
* Return the {#link Bucket} instance to connect to.
*
* #throws Exception on Bean construction failure.
*/
#Override
#Bean(destroyMethod = "close", name = BeanNames.COUCHBASE_BUCKET)
public Bucket couchbaseClient() throws Exception {
//#Bean method can use another #Bean method in the same #Configuration by directly invoking it
return couchbaseCluster().openBucket(getBucketName());
}
private KeyStore createKeyStore(final Environment environment, final ResourceLoader resourceLoader) {
return loadKeyStore(environment, resourceLoader, PROPERTY_SSL_KEYSTORE_FILE, PROPERTY_SSL_KEYSTORE_PASSWORD);
}
private KeyStore createTrustStore(final Environment environment, final ResourceLoader resourceLoader) {
return loadKeyStore(environment, resourceLoader, PROPERTY_SSL_TRUSTSTORE_FILE, PROPERTY_SSL_TRUSTSTORE_PASSWORD);
}
private KeyStore loadKeyStore(final Environment environment,
final ResourceLoader resourceLoader,
final String fileProperty,
final String passwordProperty) {
String file = environment.getProperty(fileProperty);
String password = environment.getProperty(passwordProperty);
if (file != null) {
Resource resource = resourceLoader.getResource(file);
if (resource != null) {
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(resource.getInputStream(), password == null ? null : password.toCharArray());
return keyStore;
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
}
return null;
}
}

Resources