Skip to content

Commit

Permalink
Merge branch 'release/v.2.7.5'
Browse files Browse the repository at this point in the history
Add xml feature #31 enhance some feature add unit test for xml feature and add handler response base on media type
  • Loading branch information
dekaulitz committed Aug 13, 2020
2 parents bee3ee2 + bc2f597 commit 01e972a
Show file tree
Hide file tree
Showing 22 changed files with 2,858 additions and 387 deletions.
147 changes: 118 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,143 @@
[![dekaulitz](https://circleci.com/gh/dekaulitz/MockyUp.svg?style=shield)](https://app.circleci.com/pipelines/github/dekaulitz/MockyUp)

<h2>Simplify The Integration as Fast as You can</h2>
<h2>MockyUp</h2>
# Simplify The Integration as Fast as You can

## MockyUp
MockyUp is an Openapi mock server that allow You to centralize the Openapi collections that can consuming You're Openapi spec and generating the mock as You want by You're own definitions.
We are using `OpenApi Spesification version 3.x.x` and bundled with <a href="https://swagger.io/tools/swagger-ui/"> Swagger UI </a><br/>
For now we only supporting json request and json response or string response.

For now we only supporting json request and xml request.

<h2>Stacks</h2>
MockyUp build from SpringBoot and Swagger library and backed with mongodb as database and UI with vue js. For UI referencing you can check <a href="https://github.com/dekaulitz/mockup-frontend">Mockup UI Repository</a>.
## Stacks
MockyUp build with SpringBoot and Swagger library and backed with mongodb as database and UI with vue js. For UI referencing you can check <a href="https://github.com/dekaulitz/mockup-frontend">Mockup UI Repository</a>.
We are creating new extension parameter on OpenApi spec for generating the mock response in this case we adding `x-examples` properties for set the mock.

<h2>Requirements</h2>
* MongoDb 4.x.x
* Java at least 1.8.x
* Docker (optional)
## Requirements

* MongoDb 4.x.x.
* Java at least 1.8.x.
* Docker (optional).


<h2>Features</h2>
Yes You can of crouse You can :
## Features
Yes You can of course You can :

* You can mock the response base on Path parameter.
* You can mock the response base on Body request parameter.
* You can mock the response base on Header request parameter.
* You can mock the response base on Query string parameter.
* You can defining the HttpCode response.
* You can defining the Header properites.
* You can defining the Header properties.

In this case You has full control for the response as You want with You're own definition base on criterias that You want to mock, and You can create mock user story base on the user properties or request properties that You want to try.
In this case You have full control for the response as You want with You're own definitions base on criterias that You want to mock, and You can create mock user story base on the user properties or request properties that You want to try.
This will helping You to try perfect integration before the real integration before You're application ready.

<h2>How to use</h2>
You can refer the example of OpenApi spec that already including the `x-examples` properties from <a href="https://raw.githubusercontent.com/dekaulitz/MockyUp/master/src/main/resources/public/example_mocking_books.json">here</a>.<br/>
Change the application properties that matched with You're machine.
For `x-examples` extension that has some attributes. They are :
## How to use
You can refer the example of OpenApi spec that already including the `x-examples` properties from <a href="https://raw.githubusercontent.com/dekaulitz/MockyUp/master/src/main/resources/public/example_mocking_books.json">here</a>
or for more context you can check the documentation about OpenApi <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md">here</a>.<br/>

We adding new extension for set mock configuration. They are:

* x-query-including, it will matching the query string request.
* x-header-including, it will matching the header request.
* x-path-including, it will matching the path parameter request
* x-default, if you defined the x-default if above criterias does'nt matched it will rendering the response by default response.

And then you run the Appilication like usuall or using `docker-compose`.
For the first time Application will check the user root is exist or not, if the user was not exist Application will create the user root with password root by default.

Do login with the user that already created. And create new spec or just paste example spec from <a href="https://raw.githubusercontent.com/dekaulitz/MockyUp/master/src/main/resources/public/example_mocking_books.json">here</a>
and open the spec and choose swagger ui and edit `testing_path` with id database and `host` to You're application host like `localhost:7070`.
And start to request.





### x-query-including,x-header-including,x-body-including and x-path-including configurations
```
[{
"property": {
"name": "client-id",// this is the field property
"value": "empty" // this is the field value
},
"response": {
"httpCode": 200, //http code
// you can add mock header response
"headers": {
"x-request-id": "this is from mock", //header response added by mock
},
// if you want to mock the response body base on media type
"content": {
"application/json": {
"response":{
// you can mock the response directly
"value": "something"
}
},
"application/xml": {
// you can mock the response by the reference
"$ref": "#/components/examples/LIST_OF_BOOKS_EMPTY_XML"
}
},
// if you want add the response body direclty without media type
"response":{
"value": "something"
}
// if you want to add the response body by the reference without media type
"$ref": "#/components/examples/LIST_OF_BOOKS_EMPTY"
}
}]
```
You should defined the configuration if you want to mocking the response base on path parameter,query parameter, header parameter and body request like above.
if the property matched with request properties will rendering the response as the response request.

### x-default configuration
```
{
"response": {
"httpCode": 200,//http code
// you can add mock header response
"headers": {
"x-request-id": "this is from mock",//header response added by mock
},
// if you want to mock the response body base on media type
"content": {
"application/json": {
"$ref": "#/components/examples/LIST_OF_BOOKS_EMPTY"
},
"application/xml": {
// you can mock the response by the reference
"$ref": "#/components/examples/LIST_OF_BOOKS_EMPTY_XML"
}
},
// if you want add the response body direclty without media type
"response":"#/components/examples/LIST_OF_BOOKS_EMPTY"
// if you want to add the response body by the reference without media type
"$ref": "#/components/examples/LIST_OF_BOOKS_EMPTY"
}
}
```
This `x-default` configuration when the request is not matched with other configuration `x-default` will rendering the responsse.

### How to integrate
MockyUp will running as mock server and mock collection server. You can directly hit the MockypUp mocking endpoint for test mocking the response.
```
http://[mockyup_hostname]/mocks/mocking/[mock_id]?path=[contract_endpoint]
```
Or do test via swagger
```
http://[mockyup_hostname]/swagger/[mock_id]
```
Before that you should add new server env on youre spec.
```
...
"servers":[
{
"url": "http://{host}/mocks/mocking/{mock_id}?path=",
"description": "Testing locally",
"variables": {
"host": {
"default": "localhost:7070"
},
"mock_id": {
"description": "mock id from database"
}
}
}
...
]
```
You can check the example configuration spec from <a href="https://raw.githubusercontent.com/dekaulitz/MockyUp/master/src/main/resources/public/example_mocking_books.json">here</a>
## Supported
<a href="https://www.jetbrains.com/?from=MockyUp"><img src="https://github.com/dekaulitz/MockyUp/blob/feature/addXmlFeature/src/main/resources/public/jetbrains-variant-2.png" height="100"/></a>


5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api -->
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
*/
public class BaseController {

private static final String XML_MEDIA_TYPE = "application/xml";
private static final String JSON_MEDIA_TYPE = "application/json";
protected Logger LOGGER = LoggerFactory.getLogger(this.getClass());

/**
Expand All @@ -35,14 +37,15 @@ public class BaseController {
*/
protected ResponseEntity<Object> generateMockResponseEntity(MockHelper mock) {
HttpHeaders httpHeaders = new HttpHeaders();
if (mock.getResponse().getHeaders() != null) {
for (Map.Entry headerMap : mock.getResponse().getHeaders().entrySet()) {
if (mock.getResponseProperty().getHeaders() != null) {
for (Map.Entry headerMap : mock.getResponseProperty().getHeaders().entrySet()) {
httpHeaders.add(headerMap.getKey().toString(), headerMap.getValue().toString());
}
}
return ResponseEntity.status(mock.getResponse().getHttpCode()).headers(httpHeaders).body(mock.getResponse().getResponse());
return ResponseEntity.status(mock.getResponseProperty().getHttpCode()).headers(httpHeaders).body(mock.getResponseProperty().getResponse());
}


/**
* handling error response with type of class exception
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,7 @@ public ResponseEntity<String> greeting() throws IOException {
*/
@RequestMapping(value = "/mocks/mocking/{id}", method = {RequestMethod.OPTIONS, RequestMethod.DELETE,
RequestMethod.POST, RequestMethod.GET, RequestMethod.HEAD, RequestMethod.PATCH, RequestMethod.PUT,
RequestMethod.TRACE},
produces = MediaType.APPLICATION_JSON_VALUE
RequestMethod.TRACE}
)
public ResponseEntity<Object> mockingPath(@NonNull @RequestParam(value = "path") String path,
@PathVariable String id, @RequestBody(required = false) String body,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -90,8 +91,8 @@ public void setUpdateMockEntity(MockVmodel body, MockEntities mockEntities) thro
* @throws UnsupportedEncodingException if encoding query string is fail
*/
public MockHelper renderingMockResponse(PathItem pathItem, HttpServletRequest request, String body, String[] openApiRoutePath, String[] paths, Components components)
throws NotFoundException, JsonProcessingException, InvalidMockException, UnsupportedEncodingException {
MockHelper mock = null;
throws NotFoundException, IOException, InvalidMockException {
MockHelper mock;

//parsing pathitem into JsonNode
//its because from the pathItem you its more easier for geting type of method request that wanted
Expand All @@ -104,6 +105,7 @@ public MockHelper renderingMockResponse(PathItem pathItem, HttpServletRequest re
if (ops.getExtensions() == null) throw new NotFoundException(ResponseCode.MOCKUP_NOT_FOUND);

//get custom extension for mock response from operation with extension [x-examples]
@SuppressWarnings("unchecked")
Map<String, Object> examples = (Map<String, Object>) ops.getExtensions().get(MockHelper.X_EXAMPLES);
if (examples == null) throw new NotFoundException(ResponseCode.MOCKUP_NOT_FOUND);

Expand All @@ -121,35 +123,45 @@ public MockHelper renderingMockResponse(PathItem pathItem, HttpServletRequest re
*/

//get mock response from path
if (extension.getKey() == MockHelper.X_PATH) {
mock = MockHelper.generateResponsePath((List<Map<String, Object>>) extension.getValue(), openApiRoutePath, paths, components);
if (extension.getKey().equals(MockHelper.X_PATH)) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> extensionValues = (List<Map<String, Object>>) extension.getValue();
mock = MockHelper.generateResponsePath(request,extensionValues, openApiRoutePath, paths, components);
if (mock != null) return mock;
}

//get mock from query string
if (extension.getKey() == MockHelper.X_QUERY) {
mock = MockHelper.generateResponseQuery(request, (List<Map<String, Object>>) extension.getValue(), components);
if (extension.getKey().equals(MockHelper.X_QUERY)) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> extensionValues = (List<Map<String, Object>>) extension.getValue();
mock = MockHelper.generateResponseQuery(request, extensionValues, components);
if (mock != null) return mock;
}

//checking the mock from the headers
if (extension.getKey() == MockHelper.X_HEADERS) {
mock = MockHelper.generateResponseHeader(request, (List<Map<String, Object>>) extension.getValue(), components);
if (extension.getKey().equals(MockHelper.X_HEADERS)) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> extensionValues = (List<Map<String, Object>>) extension.getValue();
mock = MockHelper.generateResponseHeader(request, extensionValues, components);
if (mock != null) return mock;
}

//checking the mock from the boyd
if (extension.getKey() == MockHelper.X_BODY) {
mock = MockHelper.generateResponseBody(request, (List<Map<String, Object>>) extension.getValue(), body, components);
if (extension.getKey().equals(MockHelper.X_BODY)) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> extensionValues = (List<Map<String, Object>>) extension.getValue();
mock = MockHelper.generateResponseBody(request, extensionValues, body, components);
if (mock != null) return mock;
}

//if there is no mock defined on path,header,query and body but default was defined
if (extension.getKey() == MockHelper.X_DEFAULT) {
mock = MockHelper.generateResponseDefault((Map<String, Object>) extension.getValue(), components);
if (mock != null) return mock;
if (extension.getKey().equals(MockHelper.X_DEFAULT)) {
@SuppressWarnings("unchecked")
Map<String, Object> extensionValues = (Map<String, Object>) extension.getValue();
mock = MockHelper.generateResponseDefault(request,extensionValues, components);
return mock;
}
}
return mock;
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.springframework.data.domain.Pageable;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;

Expand Down Expand Up @@ -99,7 +100,7 @@ public interface MockInterface {
* @throws UnsupportedEncodingException when the contract is not valid structure
* @throws InvalidMockException when the contract is not valid with the request
*/
MockHelper getMockMocking(HttpServletRequest request, String path, String id, String body) throws NotFoundException, JsonProcessingException, UnsupportedEncodingException, InvalidMockException;
MockHelper getMockMocking(HttpServletRequest request, String path, String id, String body) throws NotFoundException, IOException, InvalidMockException;

/**
* add user to mock
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.List;
Expand Down Expand Up @@ -196,7 +197,7 @@ public List<DtoMockupDetailVmodel> getDetailMockUpIdByUserAccess(String id, Auth
* @throws InvalidMockException when the contract is not valid with the request
*/
@Override
public MockHelper getMockMocking(HttpServletRequest request, String path, String id, String body) throws NotFoundException, JsonProcessingException, UnsupportedEncodingException, InvalidMockException {
public MockHelper getMockMocking(HttpServletRequest request, String path, String id, String body) throws NotFoundException, IOException, InvalidMockException {
//find the collection base on mock id
Optional<MockEntities> mockEntities = this.mockRepository.findById(id);
if (!mockEntities.isPresent())
Expand Down Expand Up @@ -331,7 +332,7 @@ private void checkAccessModification(MockEntities mockEntities, AuthenticationPr
* @throws JsonProcessingException
*/
private MockHelper validatePathWithOpenApiPaths(HttpServletRequest request, String body, OpenAPI openAPI, String[] extractPathRequest)
throws UnsupportedEncodingException, InvalidMockException, NotFoundException, JsonProcessingException {
throws IOException, InvalidMockException, NotFoundException {
//iterate all pathitem from openApi
for (Map.Entry<String, PathItem> entry : openAPI.getPaths().entrySet()) {
//get path route from pathItem from openapi path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.http.MediaType;

import java.util.Map;

Expand All @@ -13,6 +14,11 @@ public class DtoMockResponseVmodel {
private int httpCode;
private Object response;
private Map<String, Object> headers;
private Object $ref;
private Map<String, Map<String, Object>> content;


private String mediaType;

public Object get$ref() {
return $ref;
Expand All @@ -22,6 +28,12 @@ public class DtoMockResponseVmodel {
this.$ref = $ref;
}

private Object $ref;

public void setMediaType(String mediaType) {
if (mediaType != null) {
String[] acceptHeader = mediaType.split(",");
if (acceptHeader.length > 1)
mediaType = MediaType.APPLICATION_JSON_VALUE;
}
this.mediaType = mediaType;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ protected void doFilterInternal(final HttpServletRequest request, final HttpServ
req.put("responseStatus", response.getStatus());
log.info("{}", this.logsMapper.logRequest(req));
MDC.remove(ConstantsRepository.REQUEST_ID);
MDC.remove(ConstantsRepository.REQUEST_TIME);
MDC.remove(ConstantsRepository.PATH_ENDPOINT);
}

private String getxRequestID(HttpServletRequest request) {
Expand Down
Loading

0 comments on commit 01e972a

Please sign in to comment.