Monday, 22 May 2017

Using HTTPIE with Spring Boot Rest Repositories

I recently got introduced to HTTPIE as a command line alternative to CURL for testing RESTful api endpoints created using @RestController annotated classes. For more information on httpie follow this link

Before we test this out lets create a very basic Spring Boot Application with classes/interfaces to verify HTTPIE. The following assumes you have a Spring Boot application already created and it has maven dependancies as follows to enable JPA, Rest Repositories, H2 and Web support

Note: We are using Spring Boot 1.5.3 here

  
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.5.3.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
 </parent>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  <java.version>1.8</java.version>
 </properties>

 <dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-rest</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-entitymanager</artifactId>
  </dependency>
  <dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <scope>runtime</scope>
  </dependency>
 </dependencies>

1. Create classes/interfaces as follows

Employee.java
  
package pivotal.io.boot.httpie.demo;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Employee
{
    @Id
    @GeneratedValue (strategy = GenerationType.AUTO)
    private Long id;

    private String firstName;
    private String lastName;
    private String job;

    public Employee()
    {
    }

    public Employee(String firstName, String lastName, String job) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.job = job;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", job='" + job + '\'' +
                '}';
    }
}

EmployeeRepository.java
  
package pivotal.io.boot.httpie.demo;

import org.springframework.data.jpa.repository.JpaRepository;

public interface EmployeeRepository extends JpaRepository <Employee, Long> {
}  

EmployeeRest.java
  
package pivotal.io.boot.httpie.demo;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping ("/api/employee")
public class EmployeeRest
{
    private static Log logger = LogFactory.getLog(EmployeeRest.class);

    @Autowired
    private EmployeeRepository employeeRepository;

    @GetMapping("/emps")
    public List<Employee> allEmployees()
    {
        return employeeRepository.findAll();
    }

    @GetMapping("/emp/{employeeId}")
    public Employee findEmployee (@PathVariable Long employeeId)
    {
        Employee emp = employeeRepository.findOne(employeeId);

        return emp;
    }

    @PostMapping("/emps")
    public Employee createEmployee(@RequestBody Employee employee)
    {
        return employeeRepository.save(employee);
    }

    @DeleteMapping("/emps/{employeeId}")
    public void deleteEmployee(@PathVariable Long employeeId)
    {
        Employee emp = employeeRepository.findOne(employeeId);
        employeeRepository.delete(emp);
        logger.info("Employee with id " + employeeId + " deleted...");
    }

}

2. Run the Spring Boot Application which will run on port localhost:8080


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.3.RELEASE)

2017-05-22 13:39:22.910  INFO 8875 --- [           main] p.i.b.h.d.HttpieSpringbootApplication    : Starting HttpieSpringbootApplication on pas-macbook with PID 8875 (/Users/pasapicella/pivotal/DemoProjects/spring-starter/pivotal/httpie-springboot/target/classes started by pasapicella in /Users/pasapicella/pivotal/DemoProjects/spring-starter/pivotal/httpie-springboot)

...

2017-05-22 13:39:25.948  INFO 8875 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2017-05-22 13:39:25.952  INFO 8875 --- [           main] p.i.b.h.d.HttpieSpringbootApplication    : Started HttpieSpringbootApplication in 3.282 seconds (JVM running for 3.676)

Now we can test HTTPIE and here are some endpoints

3. Here are some examples with output

** All Employees **

pasapicella@pas-macbook:~$ http http://localhost:8080/api/employee/emps
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Date: Mon, 22 May 2017 01:26:43 GMT
Transfer-Encoding: chunked

[
    {
        "firstName": "pas",
        "id": 1,
        "job": "CEO",
        "lastName": "Apicella"
    },
    {
        "firstName": "lucia",
        "id": 2,
        "job": "CIO",
        "lastName": "Apicella"
    },
    {
        "firstName": "lucas",
        "id": 3,
        "job": "MANAGER",
        "lastName": "Apicella"
    },
    {
        "firstName": "siena",
        "id": 4,
        "job": "CLERK",
        "lastName": "Apicella"
    }
]

** Find Employee by {employeeId} **

pasapicella@pas-macbook:~$ http http://localhost:8080/api/employee/emp/1
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Date: Mon, 22 May 2017 01:31:32 GMT
Transfer-Encoding: chunked

{
    "firstName": "pas",
    "id": 1,
    "job": "CEO",
    "lastName": "Apicella"
}

** POST new employee **

pasapicella@pas-macbook:~$ http POST http://localhost:8080/api/employee/emps firstName=john lastName=black job=CLERK
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Date: Mon, 22 May 2017 02:32:34 GMT
Transfer-Encoding: chunked

{
    "firstName": "john",
    "id": 5,
    "job": "CLERK",
    "lastName": "black"
}

** POST with updated employee object **

pasapicella@pas-macbook:~$ http POST http://localhost:8080/api/employee/emps id:=5 firstName=john lastName=black job=CLEANER
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Date: Mon, 22 May 2017 02:36:06 GMT
Transfer-Encoding: chunked

{
    "firstName": "john",
    "id": 5,
    "job": "CLEANER",
    "lastName": "black"
}

** Delete employee with {employeeId} 5 **

pasapicella@pas-macbook:~$ http DELETE http://localhost:8080/api/employee/emps/5
HTTP/1.1 200
Content-Length: 0
Date: Mon, 22 May 2017 02:36:56 GMT

No comments: