Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(java): ✨ add feature for api response json schema validation #92

Merged
merged 15 commits into from
Jun 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions core-java/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@
<artifactId>ok2curl</artifactId>
<version>0.8.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.everit.json/org.everit.json.schema -->
<dependency>
<groupId>org.everit.json</groupId>
<artifactId>org.everit.json.schema</artifactId>
<version>1.5.1</version>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import static com.github.wasiqb.boyka.enums.Messages.INVALID_HEADER_KEY;
import static com.github.wasiqb.boyka.enums.Messages.NO_BODY_TO_PARSE;
import static com.github.wasiqb.boyka.enums.Messages.RESPONSE_SCHEMA_NOT_MATCHING;
import static com.google.common.truth.Truth.assertThat;
import static com.jayway.jsonpath.JsonPath.compile;
import static com.jayway.jsonpath.JsonPath.parse;
Expand All @@ -27,6 +28,7 @@

import java.util.Map;

import com.github.wasiqb.boyka.config.api.ApiSetting;
import com.github.wasiqb.boyka.exception.FrameworkError;
import com.google.common.truth.BooleanSubject;
import com.google.common.truth.IntegerSubject;
Expand All @@ -36,6 +38,11 @@
import lombok.Getter;
import lombok.ToString;
import org.apache.logging.log4j.Logger;
import org.everit.json.schema.Schema;
import org.everit.json.schema.ValidationException;
import org.everit.json.schema.loader.SchemaLoader;
import org.json.JSONObject;
import org.json.JSONTokener;

/**
* Response container class.
Expand All @@ -58,6 +65,7 @@ public class ApiResponse {
private long sentRequestAt;
private int statusCode;
private String statusMessage;
private ApiSetting apiSetting;

/**
* Get response body field data.
Expand Down Expand Up @@ -170,6 +178,31 @@ public StringSubject verifyTextField (final String expression) {
return assertThat (getResponseData (expression));
}

/**
* Verify schema of response.
*
* @param schemaName String expression
*/
public void verifySchema(final String schemaName) {
LOGGER.traceEntry();
LOGGER.info("Verifying Response Schema");
try {

final Schema schema = SchemaLoader.load(new JSONObject(
new JSONTokener(requireNonNull(getClass().getClassLoader().getResourceAsStream(
this.apiSetting.getSchemaPath() + schemaName)))));
schema.validate(new JSONObject(getBody()));
} catch (ValidationException e) {
LOGGER.info(e.getMessage());
e.getCausingExceptions().stream()
.map(ValidationException::getMessage)
.forEach(LOGGER::info);
throw new FrameworkError(RESPONSE_SCHEMA_NOT_MATCHING.getMessage(),e);
}
LOGGER.info("API response schema validation successfully verified");
LOGGER.traceExit ();
}

private DocumentContext jsonPath () {
LOGGER.traceEntry ();
return LOGGER.traceExit (parse (requireNonNull (this.body, NO_BODY_TO_PARSE.getMessage ())));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ public class ApiSetting {
private int port;
private int readTimeout = 5;
private int writeTimeout = 5;
}
private String schemaPath;
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ public enum Messages {
*/
USER_NAME_REQUIRED_FOR_CLOUD ("User name is required for cloud execution..."),
/**
* Schema validation assert failure
*/
RESPONSE_SCHEMA_NOT_MATCHING("Schema validation assert failure..."),
/**
* No such key found
*/
INVALID_HEADER_KEY ("No such key {0} found...");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,17 +254,18 @@ private ApiResponse parseResponse (final Response res) {
res.headers ()
.forEach (entry -> headers.put (entry.getFirst (), entry.getSecond ()));
try {
return LOGGER.traceExit (ApiResponse.createResponse ()
.request (parseRequest (res.request ()))
.statusCode (res.code ())
.statusMessage (res.message ())
.sentRequestAt (res.sentRequestAtMillis ())
.headers (headers)
.networkResponse (parseResponse (res.networkResponse ()))
.previousResponse (parseResponse (res.priorResponse ()))
.receivedResponseAt (res.receivedResponseAtMillis ())
.body (requireNonNullElse (res.body (), ResponseBody.create (EMPTY, parse (JSON.getType ()))).string ())
.create ());
return LOGGER.traceExit (ApiResponse.createResponse()
.request(parseRequest(res.request()))
.statusCode(res.code())
.statusMessage(res.message())
.sentRequestAt(res.sentRequestAtMillis())
.headers(headers).networkResponse(parseResponse(res.networkResponse()))
.apiSetting(this.apiSetting)
.networkResponse (parseResponse (res.networkResponse ()))
.previousResponse (parseResponse (res.priorResponse ()))
.receivedResponseAt (res.receivedResponseAtMillis ())
.body (requireNonNullElse (res.body (), ResponseBody.create (EMPTY, parse (JSON.getType ()))).string ())
.create ());
} catch (final IOException e) {
LOGGER.catching (e);
throw new FrameworkError (ERROR_PARSING_RESPONSE_BODY.getMessage (), e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@ public void testGetUser () {
.isEqualTo ("application/json; charset=utf-8");
response.verifyTextField ("data.first_name")
.isEqualTo ("Janet");
response.verifyTextField ("data.last_name")
.isEqualTo ("Weaver");
response.verifyTextField("data.last_name")
.isEqualTo("Weaver");
response.verifySchema("get-user-schema.json");
}

/**
Expand Down Expand Up @@ -123,8 +124,9 @@ public void testPatchUser () {
.isEqualTo (user.getName ());
response.verifyTextField ("job")
.isEqualTo (user.getJob ());
response.verifyTextField ("updatedAt")
.isNotNull ();
response.verifyTextField("updatedAt")
.isNotNull();
response.verifySchema("patch-user-schema.json");
}

/**
Expand All @@ -151,8 +153,9 @@ public void testPutUser () {
.isEqualTo (user.getName ());
response.verifyTextField ("job")
.isEqualTo (user.getJob ());
response.verifyTextField ("updatedAt")
.isNotNull ();
response.verifyTextField("updatedAt")
.isNotNull();
response.verifySchema("put-user-schema.json");
}

/**
Expand Down Expand Up @@ -180,7 +183,8 @@ public void testUserCreation () {
.isEqualTo (user.getName ());
response.verifyTextField ("job")
.isEqualTo (user.getJob ());
response.verifyTextField ("createdAt")
.isNotNull ();
response.verifyTextField("createdAt")
.isNotNull();
response.verifySchema("create-user-schema.json");
}
}
3 changes: 2 additions & 1 deletion core-java/src/test/resources/boyka-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@
"logging": {
"request": true,
"response": true
}
},
"schema_path": "schema/"
}
}
}
33 changes: 33 additions & 0 deletions core-java/src/test/resources/schema/create-user-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"$schema": "http://json-schema.org/draft-06/schema#",
"$ref": "#/definitions/Welcome10",
"definitions": {
"Welcome10": {
"type": "object",
"additionalProperties": false,
"properties": {
"job": {
"type": "string"
},
"name": {
"type": "string"
},
"id": {
"type": "string",
"format": "integer"
},
"createdAt": {
"type": "string",
"format": "date-time"
}
},
"required": [
"createdAt",
"id",
"job",
"name"
],
"title": "Welcome10"
}
}
}
80 changes: 80 additions & 0 deletions core-java/src/test/resources/schema/get-user-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
"$schema": "http://json-schema.org/draft-06/schema#",
"$ref": "#/definitions/Welcome2",
"definitions": {
"Welcome2": {
"type": "object",
"additionalProperties": false,
"properties": {
"data": {
"$ref": "#/definitions/Data"
},
"support": {
"$ref": "#/definitions/Support"
}
},
"required": [
"data",
"support"
],
"title": "Welcome2"
},
"Data": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "integer"
},
"email": {
"type": "string"
},
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
},
"avatar": {
"type": "string",
"format": "uri",
"qt-uri-protocols": [
"https"
],
"qt-uri-extensions": [
".jpg"
]
}
},
"required": [
"avatar",
"email",
"first_name",
"id",
"last_name"
],
"title": "Data"
},
"Support": {
"type": "object",
"additionalProperties": false,
"properties": {
"url": {
"type": "string",
"format": "uri",
"qt-uri-protocols": [
"https"
]
},
"text": {
"type": "string"
}
},
"required": [
"text",
"url"
],
"title": "Support"
}
}
}
28 changes: 28 additions & 0 deletions core-java/src/test/resources/schema/patch-user-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"$schema": "http://json-schema.org/draft-06/schema#",
"$ref": "#/definitions/Welcome2",
"definitions": {
"Welcome2": {
"type": "object",
"additionalProperties": false,
"properties": {
"job": {
"type": "string"
},
"name": {
"type": "string"
},
"updatedAt": {
"type": "string",
"format": "date-time"
}
},
"required": [
"job",
"name",
"updatedAt"
],
"title": "Welcome2"
}
}
}
28 changes: 28 additions & 0 deletions core-java/src/test/resources/schema/put-user-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"$schema": "http://json-schema.org/draft-06/schema#",
"$ref": "#/definitions/Welcome5",
"definitions": {
"Welcome5": {
"type": "object",
"additionalProperties": false,
"properties": {
"job": {
"type": "string"
},
"name": {
"type": "string"
},
"updatedAt": {
"type": "string",
"format": "date-time"
}
},
"required": [
"job",
"name",
"updatedAt"
],
"title": "Welcome5"
}
}
}
10 changes: 10 additions & 0 deletions website/docs/api/builders/api-response.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,16 @@ This parameter expects a valid `key` value.

Returns the [`StringSubject`][string-subject] object.

## `verifySchema` {#verify-schema}

This method will verify the api response body json schema.

### Parameters

#### `schemaName`

This parameter expects a valid `schemaName` value. The schemaName is a json file containing the expected json schema version stored at location `src/test/resources/schema/<schameName>.json`.

[boolean-subject]: https://truth.dev/api/latest/com/google/common/truth/BooleanSubject.html#method.summary
[string-subject]: https://truth.dev/api/latest/com/google/common/truth/StringSubject.html#method.summary
[int-subject]: https://truth.dev/api/latest/com/google/common/truth/IntegerSubject.html#method.summary
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Following are the methods exposed in `ApiResponse` class to verify the response
- `verifyStatusCode`: Verifies the status code of response.
- `verifyStatusMessage`: Verifies the status message of response.
- `verifyTextField`: Verifies the text field in response body.
- `verifySchema`: Verifies the json schema of resonse body.

### Methods to get response data

Expand Down
3 changes: 2 additions & 1 deletion website/docs/framework-docs/guides/api/setup-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ Let's see how to set configuration in the configuration file for API end-points.
"logging": {
"request": true,
"response": true
}
},
"schema_path":"schema/"
}
}
}
Expand Down
Loading