Skip to main content

Spring5 + Rest In Agile (1)



Spring5 + Rest In Agijle  (1)
Agile is used widely during developer’s daily life. Although it was abused a lot to hold unnecessary daily meeting to update status. But it is definitely not the reason why Agile is used at initial beginning. 
Per personal experience, I just apply the agile and tdd in our daily work even at the initial step of project as coder. 

Create Spring5 With Java 11
The first step is just go https://start.spring.io/ and create the baseline of spring5 project.E 

The generated code will like like import the project into IntelliJ Idea
The most basic idea of agile in coding way is to have each step of small and correct. There is no big jump or surprise in the whole SDLC.
At first, make sure the our first step is passed.
Run “./mvnw clean install”
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 24.337 s
[INFO] Finished at: 2018-12-14T17:11:00-05:00
[INFO] ------------------------------------------------------------------------

Now we hit the first step of agile, make project works.

Create DemoController
package org.lz.boilerplate.springrest.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("api/demo")
public class DemoController {

    public static final String HELLOWORLD = "helloworld";

    @GetMapping("hello")
    public String hello(){
        return HELLOWORLD;
    }
}

Create UnitTest Code
We write out the firs unit test to make sure the our code still work.
The test code will start the tomcat with random port and call it.
We can either use mockmvc to test the controller directly without web server started.
But this paper will focus more about the whole code infrastructure build up, testing with full sever will be better way. 
package org.lz.boilerplate.springrest;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringRestApplicationTests {

    public static final String DEMO_HELLO_API = "/api/demo/hello";
    public static final String DEMO_HELLO_API_EXPECTED = "helloworld";

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    public void callHelloAPIOk() {
        String body = this.restTemplate.getForObject(DEMO_HELLO_API, String.class);
        Assert.assertEquals(DEMO_HELLO_API_EXPECTED, body);
    }
}


Run with “./mvnw test”

[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.749 s - in 
org.lz.boilerplate.springrest.SpringRestApplicationTests
2018-12-14 23:11:29.919  INFO 46526 --- [       Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor  : 
Shutting down ExecutorService 'applicationTaskExecutor'
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------

We are still at good status.
Now I don’t like the test name, I will refactor the name to “DemoRestApiTests” to self introduce itself. 


Introduce the Spring Security OAuth
We will start the from test case as the first step 2 have test cases 
@Test
public void callHelloApiFailIfNoCredential() {
    ResponseEntity<String> resp = this.restTemplate
            .exchange(API_DEMO_HELLO, HttpMethod.GET, null, String.class );
    Assert.assertEquals(HttpStatus.UNAUTHORIZED, resp.getStatusCode());
}

@Test
public void callHelloApiOk() {

    ResponseEntity<String> resp = this.restTemplate.withBasicAuth("user", "password")
            .exchange(API_DEMO_HELLO, HttpMethod.GET, null, String.class );
    Assert.assertEquals(HttpStatus.OK, resp.getStatusCode());
    Assert.assertEquals(HELLO_API_EXPECTED, resp.getBody());
}


And we got failed test case. 
The first package we added should be package below:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

Create WebSecurityConfig per Spring5’s reference docs:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("admin").password("{noop}password").roles("ADMIN").and()
                .withUser("user").password("{noop}password").roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .httpBasic();

    }
}

Or in the more generic way, we create a UserDetailsService for customized data access.
Like 
@Bean
public UserDetailsService userDetailsService() throws Exception {
    // ensure the passwords are encoded properly
    User.UserBuilder users = User.withDefaultPasswordEncoder();
    InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
    manager.createUser(users.username("user").password("password").roles("USER").build());
    manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build());
    return manager;
}

Finally, we still pass all test cases. It means our code are still safe to checkin in day 1.
Next day let’s do it together to add JWT. The idea will be still same and simple: test for each tiny step to make sure our code are workable and reliable. 

Comments

Popular posts from this blog

How to fix "ValueError when trying to compile python module with VC Express"

When I tried to compile the python, I always get compile issue as following: ------------ ... File "C:\Python26\lib\ distutils\msvc9compiler.py ", line 358, in initialize vc_env = query_vcvarsall(VERSION, plat_spec) File "C:\Python26\lib\ distutils\msvc9compiler.py ", line 274, in query_vcvarsall raise ValueError(str(list(result.keys()))) ValueError: [u'path'] --------------------- Python community discussed a lot but no solution: http://bugs.python.org/issue7511 The root cause is because the latest visual studio change the *.bat file a lot especially on 64bit env. The python 2.7 didn't update the path accordingly. Based on the assumption above, the following solution worked for me. To install Visual Studio 2008 Express Edition with all required components: 1. Install Microsoft Visual Studio 2008 Express Edition. The main Visual Studio 2008 Express installer is available from (the C++ installer name is vcsetup.exe): https://ww

How to convert the ResultSet to Stream

Java 8 provided the Stream family and easy operation of it. The way of pipeline usage made the code clear and smart. However, ResultSet is still go with very legacy way to process. Per actual ResultSet usage, it is really helpful if converted as Stream. Here is the simple usage of above: StreamUtils.uncheckedConsumer is required to convert the the SQLException to runtimeException to make the Lamda clear.

Interview for System Design 1: Designing a URL Shortening service like TinyURL.

Problem:  This service will provide short aliases redirecting to long URLs. Step 1: Requirement Analysis Understand the the basic core features: 1. create short url from long url. 2. get the long url from  the short url.  Nice to have feature: 3. will url get expired in certain time? 4. could user define their customized short url? here is some questions need to clarify:  1. How long we need keep the url?  (it will have impact on storage, it is very import to understand to how long will the data be if such data will be stored in local storage). 2. Do we allow N : 1 or only 1: 1 mapping? (have impact about algorithm and data storage.  Step 2:   Estimation Of  Resource Usage common resources: data storage || web services: QPS Let's the estimation right now:  Assume DAU is about 500M,  Create: and one user will create new one item every 5 days. so the total creation per Second will be a. yearly new record: 500M/5 * 365 ~ 50G, new records a. monthly storage: 500M/5 * 100  * 30 = 100M *