Skip to content

Commit

Permalink
Merge branch 'master' into feature/issue-145
Browse files Browse the repository at this point in the history
  • Loading branch information
bharath8121 committed Jan 17, 2024
2 parents ad89f5e + 69918ab commit e84f684
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 24 deletions.
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
> Don't write any database access code, Install DB2Rest instead.
DB2Rest is an [Apache 2.0 Licensed](https://github.com/kdhrubo/db2rest/blob/master/LICENSE) open-source low-code middleware that provides secure and blazing fast data access layer over
your existing or new databases. You can connect to the most widely used databases like PostgreSQL, MySQL, Oracle, SQL Server, MongoDB to build a REST API in minutes without writing any code.
your existing or new databases. You can connect to the most widely used databases like PostgreSQL, MySQL, Oracle, SQL Server, and MongoDB to build a REST API in minutes without writing any code.
You can now focus on building business logic and beautiful user interfaces at speed.


Expand All @@ -25,26 +25,26 @@ You can now focus on building business logic and beautiful user interfaces at sp
<[email protected]>


# How it works?
# How does it work?

![DB2Rest- How it works?](assets/db2rest-hiw.png "DB2Rest")


The diagram above shows an application architecture with DB2Rest. DB2Rest provides secure access to the database as REST API within seconds of installation/deployment.
The business logic can be written in your favorite technology frameworks for Java, PHP, Node, .NET or using any serverless framework. The business logic layer uses the database access layer (DBAL) provided
by DB2Rest to query and modify data. The user experience layer can be developed using popular front-end frameworks or low code/no-code platforms. This layer can make use of the business logic layer or directly access secure data layer provided by DB2Rest.
The business logic can be written in your favorite technology frameworks for Java, PHP, Node, .NET, or using any serverless framework. The business logic layer uses the database access layer (DBAL) provided
by DB2Rest to query and modify data. The user experience layer can be developed using popular front-end frameworks or low-code/no-code platforms. This layer can make use of the business logic layer or directly access secure data layer provided by DB2Rest.

## Benefits

- No code, no SQL knowledge required, instead use simple REST Query Language (RQL) to retrieve data.
- Accelerate application development by 30x.
- Unlock databases - secure REST API access for legacy data.
- Blazing fast - No ORM, Single SQL Statement, 1 Database round-trip, does not use code generation.
- Support for advanced custom queries, bulk data insert, remote stored procedure calls.
- Support for advanced custom queries, bulk data insert, and remote stored procedure calls.
- Best practices for transaction management, connection pooling, encryption, security - RBAC / data entitlement.
- Deploy and run anywhere - on premise, VM, Kubernetes, any cloud.
- Deploy and run anywhere - on-premise, VM, Kubernetes, or any cloud.
- Zero downtime - adjusts to your evolving database schema.
- Compatible with Devops processes.
- Compatible with DevOps processes.


# Installation
Expand All @@ -66,7 +66,7 @@ Open JDK can be downloaded from [here](https://jdk.java.net/21/). This article f
Now that you have successfully downloaded, installed, and verified Java 21, the next step is to get DB2Rest. DB2Rest is shipped
as a single executable Java Archive or jar file So it's super easy to get up and running under a minute.

To download the latest edition(v-0.0.8) of DB2Rest click [here](https://www.docker.com/get-started/).
To download the latest edition(v-0.0.8) of DB2Rest click [here](https://download.db2rest.com/db2rest-0.0.8.jar).

### 3. Run DB2Rest.

Expand Down
4 changes: 4 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<dependency>
<groupId>com.ibm.db2</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,40 @@
package com.homihq.db2rest.exception;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
HttpHeaders headers,
HttpStatusCode status, WebRequest request) {
var body = new LinkedHashMap<>();
body.put("type", "https://github.com/kdhrubo/db2rest/invalid-arguments");
body.put("title", "Invalid arguments in the request");
body.put("status", status.value());
var errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(error -> Map.of(error.getField(), Objects.requireNonNull(error.getDefaultMessage())))
.toList();
body.put("detail", errors);
body.put("instance", ((ServletWebRequest) request).getRequest().getRequestURI());
body.put("errorCategory", "Invalid-Arguments");
body.put("timestamp", Instant.now());
return new ResponseEntity<>(body, headers, status);
}

}
13 changes: 11 additions & 2 deletions src/main/java/com/homihq/db2rest/rest/read/ReadController.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package com.homihq.db2rest.rest.read;

import com.homihq.db2rest.rest.read.model.QueryRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import static org.springframework.web.bind.ServletRequestUtils.*;

import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;


Expand Down Expand Up @@ -39,7 +44,11 @@ public Object findByJoinTable(@PathVariable String tableName,

return readService.findAll(schemaName, tableName,select, filter, pageable, sort);
}


@PostMapping(value = "/query", consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> findByCustomQuery(@RequestBody @Valid QueryRequest queryRequest) {
log.debug("Execute SQL statement {} with params {}", queryRequest.getSql(), queryRequest.getParams());
return ResponseEntity.ok(readService.findByCustomQuery(queryRequest));
}

}
10 changes: 6 additions & 4 deletions src/main/java/com/homihq/db2rest/rest/read/ReadService.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.homihq.db2rest.rest.read;

import com.homihq.db2rest.rest.read.helper.*;
import com.homihq.db2rest.rest.read.model.QueryRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Pageable;
Expand Down Expand Up @@ -46,8 +47,9 @@ public Object findAll(String schemaName, String tableName, String select, String

}





Object findByCustomQuery(QueryRequest queryRequest) {
return queryRequest.isSingle() ?
namedParameterJdbcTemplate.queryForMap(queryRequest.getSql(), queryRequest.getParams()) :
namedParameterJdbcTemplate.queryForList(queryRequest.getSql(), queryRequest.getParams());
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/homihq/db2rest/rest/read/model/QueryRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.homihq.db2rest.rest.read.model;

import jakarta.validation.constraints.NotEmpty;
import lombok.Builder;
import lombok.Data;

import java.util.Map;

@Data
@Builder
public class QueryRequest {

@NotEmpty(message = "Sql statement cannot be empty")
private String sql;

private Map<String, Object> params;

private boolean isSingle;
}
84 changes: 74 additions & 10 deletions src/test/java/com/homihq/db2rest/rest/PgReadControllerTest.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
package com.homihq.db2rest.rest;

import com.homihq.db2rest.PostgreSQLBaseIntegrationTest;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;

import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.*;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.restdocs.request.RequestDocumentation.*;

import static org.hamcrest.Matchers.*;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

class PgReadControllerTest extends PostgreSQLBaseIntegrationTest {

@Test
Expand All @@ -27,4 +25,70 @@ void findAllFilms() throws Exception {
.andDo(print())
.andDo(document("pg-get-all-films"));
}

@Test
@DisplayName("Query returns single result")
void query_returns_single_result() throws Exception {
var json = """
{
"sql": "SELECT FIRST_NAME,LAST_NAME FROM ACTOR WHERE ACTOR_ID = :id",
"params" : {
"id" : 1
},
"single" : true
}
""";

mockMvc.perform(post("/query").contentType(MediaType.APPLICATION_JSON).characterEncoding("utf-8")
.content(json).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.first_name", equalTo("PENELOPE")))
.andDo(print())
.andDo(document("pg-create-a-film"));
}

@Test
@DisplayName("Query returns list of results")
void query_returns_list_of_results() throws Exception {
var json = """
{
"sql": "SELECT FIRST_NAME,LAST_NAME FROM ACTOR WHERE ACTOR_ID IN (:id1, :id2)",
"params" : {
"id1" : 1,
"id2" : 2
},
"single" : false
}
""";

mockMvc.perform(post("/query").contentType(MediaType.APPLICATION_JSON).characterEncoding("utf-8")
.content(json).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.*", hasSize(2)))
.andExpect(jsonPath("$[0].first_name", equalTo("PENELOPE")))
.andExpect(jsonPath("$[1].last_name", equalTo("WAHLBERG")))
.andDo(print())
.andDo(document("pg-create-a-film"));
}

@Test
@DisplayName("Query returns 400 bad request error")
void query_returns_400_bad_request() throws Exception {
var json = """
{
"sql": "",
"params" : {
"id1" : 1
},
"single" : false
}
""";

mockMvc.perform(post("/query").contentType(MediaType.APPLICATION_JSON).characterEncoding("utf-8")
.content(json).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.status", is(400)))
.andDo(print())
.andDo(document("pg-create-a-film"));
}
}

0 comments on commit e84f684

Please sign in to comment.