Adding path to base url in Spring Rest Docs? - kotlin

I have following configuration to use with Rest Docs:
webTestClient = buildWebClient().mutate()
.filter(documentationConfiguration(restDocumentation))
.baseUrl("https://api.my-domain.com/")
.build()
In my case I use path prefix to my service - service/foo since I use k8s ingress and my service is served on path offset.
Is there a way to insert such prefix without modifying production code?
Related documentation fragment:
https://docs.spring.io/spring-restdocs/docs/current/reference/html5/#configuration-uris-webtestclient

To document another URI than the one called to generate documentation, you have to write your own OperationPreprocessor. There are some predefined like Preprocessors.modifyUris but it does not allow to modify the request path.
Check below the webTestClient configuration and the URIUpdaterOperationRequest class. Code is available on GitHub: https://github.com/Query-Interface/SO-Answers/blob/master/java/spring/rest-docs-modify-uripath/src/test/java/com/example/demo/DemoApplicationTests.java
public void init() throws Exception {
final URIUpdaterPreprocessor preprocessor = new URIUpdaterPreprocessor();
webTestClient = webTestClient.mutate()
.filter((documentationConfiguration(this.restDocumentation)
.operationPreprocessors()
.withRequestDefaults(preprocessor)
.withResponseDefaults(prettyPrint()))
)
.build();
}
private static final class URIUpdaterPreprocessor
implements OperationPreprocessor {
#Override
public OperationRequest preprocess(OperationRequest request) {
return new URIUpdaterOperationRequest(request);
}
#Override
public OperationResponse preprocess(OperationResponse response) {
return response;
}
}
private static final class URIUpdaterOperationRequest
implements OperationRequest {
private OperationRequest delegate;
public URIUpdaterOperationRequest(OperationRequest request) {
delegate = request;
}
public byte[] getContent() {
return delegate.getContent();
}
public String getContentAsString() {
return delegate.getContentAsString();
}
public HttpHeaders getHeaders() {
return delegate.getHeaders();
}
public HttpMethod getMethod() {
return delegate.getMethod();
}
public Parameters getParameters() {
return delegate.getParameters();
}
public Collection<OperationRequestPart> getParts() {
return delegate.getParts();
}
public URI getUri() {
URI sourceUri = delegate.getUri();
UriComponentsBuilder builder = UriComponentsBuilder.fromUri(sourceUri);
return builder
.host(sourceUri.getHost())
.replacePath("/service/foo"+sourceUri.getPath())
.build().toUri();
}
public Collection<RequestCookie> getCookies() {
return delegate.getCookies();
}
}
I think another possibilty is to update the mustache templates so as to add a prefix before all request path references. The default templates are located here on github.

Related

I know we can't call an extension method with the same name. But what are the other ways to achieve this?

I know we can't call an extension method with the same name. But what are the other ways to achieve this? I want to call an extension method which I created to replace an another api method(sealed) which is used by dozen's of classes so making a change in those classes is not feasible.
Is there any design pattern or solution which can solve this problem without modifying the existing consumer classes?
Below is the code sample:-
public class RestAppClassThatUsesRestClient
{
private IRestClient _3rdPartyRestClient;
private ILogger _logger;
private IConfiguration _configuration;
public RestAppClassThatUsesRestClient(IRestClient restClient, ILogger logger, IConfiguration configuration)
{
_3rdPartyRestClient = restClient;
_logger = logger;
_configuration = configuration;
}
public Task<TModel> GetSomething<TModel>(string url)
{
return _3rdPartyRestClient.RetryLogic(url);
}
}
I want to call below static class method without modifying the code in above class which is currently calling the same method from 3rd party library.
/// <summary>
/// Retry logic static class for extending the functionalities of IRestClient client
/// </summary>
public static class RestClientExtentions
{
public static Task<TModel> RetryLogic(this IRestClient restClient, string url)
{
int retryCount = 0;
int maxRetryAtttemp = configuration.GetSection("AppSettings").GetSection("MaxRetryAttempt").Value; //maxRetry value need to be set it can be pick from any config file or other sources
while (retryCount < maxRetryAtttemp)
{
try
{
return await restClient.Get<TModel>(url);
}
catch (System.Net.WebException wex)
{
if (retryCount < maxRetryAtttemp)
{
retryCount++;
Thread.Sleep(configuration.GetSection("AppSettings").GetSection("Timeout").Value);//time value need to be set it can be pick from any config file or other sources
}
else
{
Console.log(_logger.Error(wex));
return default(TModel);
}
}
catch (Exception ex)
{
throw ex;
}
}
return default(TModel);
}
}
Your can implement class, wich implementing IRestClient interface and inject to constructor client classes
class RestClientExt : IRestClient {
private IRestClient _restClient;
public RestClientExt(IRestClient restFromLib) {
_restClient = restFromLib;
}
public async Task<TModel> Get(string url)
{
int retryCount = 0;
int maxRetryAtttemp = configuration.GetSection("AppSettings").GetSection("MaxRetryAttempt").Value; //maxRetry value need to be set it can be pick from any config file or other sources
while (retryCount < maxRetryAtttemp)
{
try
{
return await _restClient.Get<TModel>(url);
}
catch (System.Net.WebException wex)
{
if (retryCount < maxRetryAtttemp)
{
retryCount++;
Thread.Sleep(configuration.GetSection("AppSettings").GetSection("Timeout").Value);//time value need to be set it can be pick from any config file or other sources
}
else
{
Console.log(_logger.Error(wex));
return default(TModel);
}
}
catch (Exception ex)
{
throw ex;
}
}
return default(TModel);
}
// Other override methods
}
// Next use RestClientExt
IRestClient restClient = new RestClientFromLib();
IRestClient restClientExt = new RestClientExt(restClient);
var client = new RestAppClassThatUsesRestClient(restClientExt, ...)

Getting noBaseStepListener error while using Serenity RestAssured

I am trying to implement Rest Assured framework with cucumber
I am facing a weird scenario that I have defined all my step definitions of my feature file then also I am getting error as below when I run my feature file:-
Step undefined
You can implement this step and 3 other step(s) using the snippet(s) below:
#Given("I create new service by using create service API data")
public void i_create_new_service_by_using_create_service_api_data() {
// Write code here that turns the phrase above into concrete actions
throw new io.cucumber.java.PendingException();
}
and When I run the same from Junit Testrunner then I get error as below :-
INFO net.serenitybdd.rest.decorators.request.RequestSpecificationDecorated - No BaseStepListener, POST /services not registered.
In my framework I am defining basepackage containing base class file which is as below :-
public class TestBase {
public static Properties propertyConfig = new Properties();
public static FileInputStream fis;
public static Response response;
public static RequestSpecification requestSpecification;
public static void loadPreConfigs(){
try {
fis = new FileInputStream("./src/test/resources/ConfigurationURLs/config.properties");
try {
propertyConfig.load(fis);
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
RestAssured.baseURI=propertyConfig.getProperty("BaseURI");
}
}
Then I have a ApiCall package which contains all class files which have request specification and respective response storing rest API calls
The APICall file is given below:-
public class PostRequestCall extends TestBase {
private static String productVal;
public static int getProductVal() {
return Integer.parseInt(productVal);
}
public static void setProductVal(String productVal) {
PostRequestCall.productVal= productVal;
}
public RequestSpecification definePostRequest(){
requestSpecification= SerenityRest.given();
requestSpecification.contentType(ContentType.JSON);
return requestSpecification;
}
public Response CreateService(String serviceName){
JSONObject jsonObject=new JSONObject();
jsonObject.put("name",serviceName);
response=definePostRequest().body(jsonObject).post(propertyConfig.getProperty("createService"));
return response;
}
}
Then I have step file which are the class file in which I define the steps of serenity given below:
public class PostRequestSteps {
PostRequestCall postRequestCall=new PostRequestCall();
#Step
public RequestSpecification setPostSpecification(){
TestBase.requestSpecification=postRequestCall.definePostRequest();
return TestBase.requestSpecification;
}
#Step
public Response setPostRequestCall(String serviceName){
TestBase.response=postRequestCall.CreateService(serviceName);
return TestBase.response;
}
}
And I have defined a package which contains all the step definition classes one such class is as below :-
public class PostRequest_StepDefinitions {
String serviceID;
#Steps
PostRequestSteps postRequestSteps=new PostRequestSteps();
#Before
public void setUp() {
TestBase.loadPreConfigs();
}
#Given("I create new service by using create service API data")
public void i_create_new_service_by_using_create_service_api_data() {
postRequestSteps.setPostSpecification();
}
#When("I provide valid name {string} for service creation")
public void i_provide_valid_name_for_service_creation(String serviceName) {
TestBase.response=postRequestSteps.setPostRequestCall(serviceName);
}
#And("I save the id of created service")
public void i_save_the_id_of_created_service() {
serviceID=TestBase.response.jsonPath().get("id").toString();
PostRequestCall.setProductVal(serviceID);
}
#Then("I validate status code {int}")
public void i_validate_status_code(int statusCode) {
Assert.assertEquals(TestBase.response.getStatusCode(),statusCode);
}
The Junit Runner file and feature files are below

How do you adjust json config in Quarkus?

I am attempting to add a mixin to the Jackson's ObjectMapper in a Quarkus project. I have some code that looks likes this:
#Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public ObjectMapperContextResolver() {
this.mapper = createObjectMapper();
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
private ObjectMapper createObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(MyModel.class, MyMixin.class);
return mapper;
}
}
This code worked perfectly in a Thorntail project I had. For some reason, Quarkus isn't picking this up, and the object mapper is not affected. Is there something different I have to do with the Quarkus CDI?
Updates
Apparently I was a little confused about the implementation. I should be using the Json-B api. I figured out how to change the configuration for Json-B and posted it below.
Instead of providing an ObjectMapper, you can provide a JsonbConfig so that you can customize serialization/deserialization.
Here is what I ended up using:
#Provider
public class JsonConfig implements ContextResolver<Jsonb> {
#Override
public Jsonb getContext(Class type) {
JsonbConfig config = new JsonbConfig();
config.withPropertyVisibilityStrategy(new IgnoreMethods());
return JsonbBuilder.create(config);
}
}
class IgnoreMethods implements PropertyVisibilityStrategy {
#Override
public boolean isVisible(Field field) {
return true;
}
#Override
public boolean isVisible(Method method) {
return false;
}
}
This allows you to customize your JsonbConfig. Here, mine specifically prevents access of methods for serialization/deserialization. On Quarkus with Panache, this prevents isPersistent from appearing in your JSON output.
In addition to the correct answer of #jsolum, here is a working provider which uses the fasterxml-annotations to check visibility of fields and methods:
#Provider
public class JsonConfig implements ContextResolver<Jsonb> {
#Override
public Jsonb getContext(Class aClass) {
JsonbConfig config = new JsonbConfig();
config.withPropertyVisibilityStrategy(new PropertyVisibilityStrategy() {
#Override
public boolean isVisible(Field field) {
JsonIgnore annotation = field.getAnnotation(JsonIgnore.class);
return annotation == null || !annotation.value();
}
#Override
public boolean isVisible(Method method) {
JsonIgnore annotation = method.getAnnotation(JsonIgnore.class);
return annotation == null || !annotation.value();
}
});
return JsonbBuilder.create(config);
}
}
JsonbConfig in Quarkus can be customized providing an ApplicationScoped instance of JsonbConfigCustomizer (taking #jsolum's answer into account):
#ApplicationScoped
public class JsonbFormattingConfig implements JsonbConfigCustomizer {
#Override
public void customize(JsonbConfig jsonbConfig) {
jsonbConfig.withPropertyVisibilityStrategy(new IgnoreMethods());
}
}
class IgnoreMethods implements PropertyVisibilityStrategy {
#Override
public boolean isVisible(Field field) {
return true;
}
#Override
public boolean isVisible(Method method) {
return false;
}
}
Source: https://quarkus.io/guides/rest-json#json-b

Injecting Dependency into Web API Controller

I want to inject unity container into WebController.
I have UnityDependencyResolver:
public class UnityDependencyResolver : IDependencyResolver
{
readonly IUnityContainer _container;
public UnityDependencyResolver(IUnityContainer container)
{
this._container = container;
}
public object GetService(Type serviceType)
{
try
{
return _container.Resolve(serviceType);
}
catch
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return _container.ResolveAll(serviceType);
}
catch
{
return new List<object>();
}
}
public void Dispose()
{
_container.Dispose();
}
}
Then, in my Global.asax I add the following line:
var container = new UnityContainer();
container.RegisterType<IService, Service>
(new PerThreadLifetimeManager()).RegisterType<IDALContext, DALContext>();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
Then, If I use the following in a Web Controller:
private IService _service;
public HomeController(IService srv)
{
_service = srv;
}
It works fine.
But I want to inject it into WebAPI Controller, so if I do it the same way:
private IService _service;
public ValuesController(IService srv)
{
_service = srv;
}
It does not work, it says that constructor is not defined.
Ok, I create one more constructor:
public ValuesController(){}
And in this case it uses only this constructor and never the one where I should inject unity container.
Please advise.
Add this in your WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Routes and other stuff here...
var container = IocContainer.Instance; // Or any other way to fetch your container.
config.DependencyResolver = new UnityDependencyResolver(container);
}
}
And if you want the same container you can keep it in a static variable, like so:
public static class IocContainer
{
private static readonly Lazy<IUnityContainer> Container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
return container;
});
public static IUnityContainer Instance
{
get { return Container.Value; }
}
}
More info can be found here:
http://www.asp.net/web-api/overview/advanced/dependency-injection
On a sidenote, I can also recommend the nuget-package Unity.Mvc. It adds a UnityWebActivator and support for PerRequestLifetimeManager.
https://www.nuget.org/packages/Unity.Mvc/

Is it possible to use one generic/abstract service in ServiceStack?

I am developing a (hopefully) RESTful API using ServiceStack.
I noticed that most of my services look the same, for example, a GET method will look something like this:
try
{
Validate();
GetData();
return Response();
}
catch (Exception)
{
//TODO: Log the exception
throw; //rethrow
}
lets say I got 20 resources, 20 request DTOs, so I got about 20 services of the same template more or less...
I tried to make a generic or abstract Service so I can create inheriting services which just implement the relevant behavior but I got stuck because the request DTOs weren't as needed for serialization.
Is there any way to do it?
EDIT:
an Example for what I'm trying to do:
public abstract class MyService<TResponse,TRequest> : Service
{
protected abstract TResponse InnerGet();
protected abstract void InnerDelete();
public TResponse Get(TRequest request)
{
//General Code Here.
TResponse response = InnerGet();
//General Code Here.
return response;
}
public void Delete(TRequest request)
{
//General Code Here.
InnerDelete();
//General Code Here.
}
}
public class AccountService : MyService<Accounts, Account>
{
protected override Accounts InnerGet()
{
throw new NotImplementedException();//Get the data from BL
}
protected override void InnerDelete()
{
throw new NotImplementedException();
}
}
To do this in the New API we've introduced the concept of a IServiceRunner that decouples the execution of your service from the implementation of it.
To add your own Service Hooks you just need to override the default Service Runner in your AppHost from its default implementation:
public virtual IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)
{
return new ServiceRunner<TRequest>(this, actionContext); //Cached per Service Action
}
With your own:
public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)
{
return new MyServiceRunner<TRequest>(this, actionContext); //Cached per Service Action
}
Where MyServiceRunner is just a custom class implementing the custom hooks you're interested in, e.g:
public class MyServiceRunner<T> : ServiceRunner<T> {
public override void OnBeforeExecute(IRequestContext requestContext, TRequest request) {
// Called just before any Action is executed
}
public override object OnAfterExecute(IRequestContext requestContext, object response) {
// Called just after any Action is executed, you can modify the response returned here as well
}
public override object HandleException(IRequestContext requestContext, TRequest request, Exception ex) {
// Called whenever an exception is thrown in your Services Action
}
}
Also for more fine-grained Error Handling options check out the Error Handling wiki page.
My solution was to add an additional layer where I can handle Logic per entity:
Base Logic Sample:
public interface IEntity
{
long Id { get; set; }
}
public interface IReadOnlyLogic<Entity> where Entity : class, IEntity
{
List<Entity> GetAll();
Entity GetById(long Id);
}
public abstract class ReadOnlyLogic<Entity> : IReadOnlyLogic<Entity> where Entity : class, IEntity, new()
{
public IDbConnection Db { get; set; }
#region HOOKS
protected SqlExpression<Entity> OnGetList(SqlExpression<Entity> query) { return query; }
protected SqlExpression<Entity> OnGetSingle(SqlExpression<Entity> query) { return OnGetList(query); }
#endregion
public List<Entity> GetAll()
{
var query = OnGetList(Db.From<Entity>());
return Db.Select(query);
}
public Entity GetById(long id)
{
var query = OnGetSingle(Db.From<Entity>())
.Where(e => e.Id == id);
var entity = Db.Single(query);
return entity;
}
}
Then we can use hooks like:
public interface IHello : IReadOnlyLogic<Hello> { }
public class HelloLogic : ReadOnlyLogic<Hello>, IHello
{
protected override SqlExpression<Hello> OnGetList(SqlExpression<Hello> query)
{
return query.Where(h => h.Name == "Something");
}
}
Finally our service only calls our logic:
public class MyServices : Service
{
IHello helloLogic;
public object Get()
{
return helloLogic.GetAll();
}
}