diff --git a/checkstyle.xml b/checkstyle.xml index cc630396..369704f6 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -329,7 +329,6 @@ - diff --git a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/codec/DefaultStringDeserializer.java b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/codec/DefaultStringDeserializer.java index e43c6302..132d81e8 100644 --- a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/codec/DefaultStringDeserializer.java +++ b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/codec/DefaultStringDeserializer.java @@ -1,5 +1,11 @@ package com.tvd12.ezyhttp.core.codec; +import com.tvd12.ezyfox.collect.Lists; +import com.tvd12.ezyfox.collect.Sets; +import com.tvd12.ezyfox.io.EzyDataConverter; +import com.tvd12.ezyfox.io.EzyDates; +import com.tvd12.ezyfox.io.EzyStrings; + import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; @@ -7,21 +13,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.tvd12.ezyfox.collect.Lists; -import com.tvd12.ezyfox.collect.Sets; -import com.tvd12.ezyfox.io.EzyDataConverter; -import com.tvd12.ezyfox.io.EzyDates; -import com.tvd12.ezyfox.io.EzyStrings; +import java.util.*; public class DefaultStringDeserializer implements StringDeserializer { @@ -49,7 +41,10 @@ public T deserialize( } @SuppressWarnings({"unchecked"}) - public T deserialize(String value, Class outType) throws IOException { + public T deserialize( + String value, + Class outType + ) throws IOException { StringMapper mapper = mappers.get(outType); if (mapper == null) { if (value == null) { @@ -70,6 +65,29 @@ public T deserialize(String value, Class outType) throws IOException { } } + @SuppressWarnings({"unchecked"}) + public T deserializeOrNull( + String value, + Class outType + ) { + StringMapper mapper = mappers.get(outType); + if (mapper == null) { + if (value == null) { + return null; + } + if (outType.isEnum()) { + return stringToEnum(value, outType); + } + } else { + try { + return (T) mapper.apply(value); + } catch (Exception e) { + return null; + } + } + return null; + } + @SuppressWarnings({"unchecked", "rawtypes"}) private T stringToEnum(String value, Class outType) { try { diff --git a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpBadRequestException.java b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpBadRequestException.java index ad683b16..c67fcba3 100644 --- a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpBadRequestException.java +++ b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpBadRequestException.java @@ -8,4 +8,8 @@ public class HttpBadRequestException extends HttpRequestException { public HttpBadRequestException(Object data) { super(StatusCodes.BAD_REQUEST, data); } + + public HttpBadRequestException(Object data, Throwable cause) { + super(StatusCodes.BAD_REQUEST, data, cause); + } } diff --git a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpConflictException.java b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpConflictException.java index 166829eb..a843b415 100644 --- a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpConflictException.java +++ b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpConflictException.java @@ -8,4 +8,8 @@ public class HttpConflictException extends HttpRequestException { public HttpConflictException(Object data) { super(StatusCodes.CONFLICT, data); } + + public HttpConflictException(Object data, Throwable cause) { + super(StatusCodes.CONFLICT, data, cause); + } } diff --git a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpForbiddenException.java b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpForbiddenException.java index 5686148b..3d335fbe 100644 --- a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpForbiddenException.java +++ b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpForbiddenException.java @@ -8,4 +8,8 @@ public class HttpForbiddenException extends HttpRequestException { public HttpForbiddenException(Object data) { super(StatusCodes.FORBIDDEN, data); } + + public HttpForbiddenException(Object data, Throwable cause) { + super(StatusCodes.FORBIDDEN, data, cause); + } } diff --git a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpInternalServerErrorException.java b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpInternalServerErrorException.java index ed47371a..1e88c8e2 100644 --- a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpInternalServerErrorException.java +++ b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpInternalServerErrorException.java @@ -8,4 +8,8 @@ public class HttpInternalServerErrorException extends HttpRequestException { public HttpInternalServerErrorException(Object data) { super(StatusCodes.INTERNAL_SERVER_ERROR, data); } + + public HttpInternalServerErrorException(Object data, Throwable cause) { + super(StatusCodes.INTERNAL_SERVER_ERROR, data, cause); + } } diff --git a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpMethodNotAllowedException.java b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpMethodNotAllowedException.java index 42d3b239..4a804187 100644 --- a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpMethodNotAllowedException.java +++ b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpMethodNotAllowedException.java @@ -8,4 +8,8 @@ public class HttpMethodNotAllowedException extends HttpRequestException { public HttpMethodNotAllowedException(Object data) { super(StatusCodes.METHOD_NOT_ALLOWED, data); } + + public HttpMethodNotAllowedException(Object data, Throwable cause) { + super(StatusCodes.METHOD_NOT_ALLOWED, data, cause); + } } diff --git a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpNotAcceptableException.java b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpNotAcceptableException.java index ed33362a..20a73b02 100644 --- a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpNotAcceptableException.java +++ b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpNotAcceptableException.java @@ -8,4 +8,8 @@ public class HttpNotAcceptableException extends HttpRequestException { public HttpNotAcceptableException(Object data) { super(StatusCodes.NOT_ACCEPTABLE, data); } + + public HttpNotAcceptableException(Object data, Throwable cause) { + super(StatusCodes.NOT_ACCEPTABLE, data, cause); + } } diff --git a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpNotFoundException.java b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpNotFoundException.java index b4bba70c..904eb6c0 100644 --- a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpNotFoundException.java +++ b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpNotFoundException.java @@ -8,4 +8,8 @@ public class HttpNotFoundException extends HttpRequestException { public HttpNotFoundException(Object data) { super(StatusCodes.NOT_FOUND, data); } + + public HttpNotFoundException(Object data, Throwable cause) { + super(StatusCodes.NOT_FOUND, data, cause); + } } diff --git a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpPaymentRequiredException.java b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpPaymentRequiredException.java index 2043d4f5..f21fc1d0 100644 --- a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpPaymentRequiredException.java +++ b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpPaymentRequiredException.java @@ -8,4 +8,8 @@ public class HttpPaymentRequiredException extends HttpRequestException { public HttpPaymentRequiredException(Object data) { super(StatusCodes.PAYMENT_REQUIRED, data); } + + public HttpPaymentRequiredException(Object data, Throwable cause) { + super(StatusCodes.PAYMENT_REQUIRED, data, cause); + } } diff --git a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpRequestException.java b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpRequestException.java index beb44271..06437a87 100644 --- a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpRequestException.java +++ b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpRequestException.java @@ -9,12 +9,25 @@ public class HttpRequestException extends RuntimeException { protected final int code; protected final Object data; - public HttpRequestException(int code, Object data) { + public HttpRequestException( + int code, + Object data + ) { super("code: " + code + ", data: " + data); this.code = code; this.data = data; } + public HttpRequestException( + int code, + Object data, + Throwable cause + ) { + super("code: " + code + ", data: " + data, cause); + this.code = code; + this.data = data; + } + @SuppressWarnings("unchecked") public T getData() { return (T) data; diff --git a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpRequestTimeoutException.java b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpRequestTimeoutException.java index a61bc63f..6861de47 100644 --- a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpRequestTimeoutException.java +++ b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpRequestTimeoutException.java @@ -8,4 +8,8 @@ public class HttpRequestTimeoutException extends HttpRequestException { public HttpRequestTimeoutException(Object data) { super(StatusCodes.REQUEST_TIMEOUT, data); } + + public HttpRequestTimeoutException(Object data, Throwable cause) { + super(StatusCodes.REQUEST_TIMEOUT, data, cause); + } } diff --git a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpTooManyRequestsException.java b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpTooManyRequestsException.java index 21bdb4b0..52f878d7 100644 --- a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpTooManyRequestsException.java +++ b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpTooManyRequestsException.java @@ -8,4 +8,8 @@ public class HttpTooManyRequestsException extends HttpRequestException { public HttpTooManyRequestsException(Object data) { super(StatusCodes.TOO_MANY_REQUESTS, data); } + + public HttpTooManyRequestsException(Object data, Throwable cause) { + super(StatusCodes.TOO_MANY_REQUESTS, data, cause); + } } diff --git a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpUnauthorizedException.java b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpUnauthorizedException.java index eeb5cf9c..50d8c35f 100644 --- a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpUnauthorizedException.java +++ b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpUnauthorizedException.java @@ -8,4 +8,8 @@ public class HttpUnauthorizedException extends HttpRequestException { public HttpUnauthorizedException(Object data) { super(StatusCodes.UNAUTHORIZED, data); } + + public HttpUnauthorizedException(Object data, Throwable cause) { + super(StatusCodes.UNAUTHORIZED, data, cause); + } } diff --git a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpUnsupportedMediaTypeException.java b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpUnsupportedMediaTypeException.java index 5671426f..b6b494ab 100644 --- a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpUnsupportedMediaTypeException.java +++ b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/exception/HttpUnsupportedMediaTypeException.java @@ -8,4 +8,8 @@ public class HttpUnsupportedMediaTypeException extends HttpRequestException { public HttpUnsupportedMediaTypeException(Object data) { super(StatusCodes.UNSUPPORTED_MEDIA_TYPE, data); } + + public HttpUnsupportedMediaTypeException(Object data, Throwable cause) { + super(StatusCodes.UNSUPPORTED_MEDIA_TYPE, data, cause); + } } diff --git a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/codec/SingletonStringDeserializerTest.java b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/codec/SingletonStringDeserializerTest.java index 07df0da1..b29942af 100644 --- a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/codec/SingletonStringDeserializerTest.java +++ b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/codec/SingletonStringDeserializerTest.java @@ -380,6 +380,35 @@ public void deserializeEnumTest() throws Exception { .deserialize("hello", MyEnum.class), MyEnum.HELLO); } + @Test + public void deserializeOrNullTest() { + // given + // when + // then + Asserts.assertEquals( + SingletonStringDeserializer.getInstance() + .deserializeOrNull("1", int.class), + 1 + ); + Asserts.assertNull( + SingletonStringDeserializer.getInstance() + .deserializeOrNull("1", void.class) + ); + Asserts.assertNull( + SingletonStringDeserializer.getInstance() + .deserializeOrNull(null, void.class) + ); + Asserts.assertNull( + SingletonStringDeserializer.getInstance() + .deserializeOrNull("hello", Long.class) + ); + Asserts.assertEquals( + SingletonStringDeserializer.getInstance() + .deserializeOrNull("WORLD", MyEnum.class), + MyEnum.WORLD + ); + } + public enum MyEnum { HELLO, WORLD } diff --git a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpBadRequestExceptionTest.java b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpBadRequestExceptionTest.java index e6dec6ee..c77e29c5 100644 --- a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpBadRequestExceptionTest.java +++ b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpBadRequestExceptionTest.java @@ -1,10 +1,9 @@ package com.tvd12.ezyhttp.core.test.exception; -import org.testng.annotations.Test; - import com.tvd12.ezyhttp.core.constant.StatusCodes; import com.tvd12.ezyhttp.core.exception.HttpBadRequestException; import com.tvd12.test.assertion.Asserts; +import org.testng.annotations.Test; public class HttpBadRequestExceptionTest { @@ -21,4 +20,23 @@ public void test() { Asserts.assertEquals(code, sut.getCode()); Asserts.assertEquals(data, sut.getData()); } + + @Test + public void withCauseTest() { + // given + int code = StatusCodes.BAD_REQUEST; + String data = "error"; + Exception cause = new Exception("test"); + + // when + HttpBadRequestException sut = new HttpBadRequestException( + data, + cause + ); + + // then + Asserts.assertEquals(code, sut.getCode()); + Asserts.assertEquals(data, sut.getData()); + Asserts.assertEquals(sut.getCause(), cause); + } } diff --git a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpConflictExceptionTest.java b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpConflictExceptionTest.java index ba86fbfe..ef052051 100644 --- a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpConflictExceptionTest.java +++ b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpConflictExceptionTest.java @@ -1,10 +1,9 @@ package com.tvd12.ezyhttp.core.test.exception; -import org.testng.annotations.Test; - import com.tvd12.ezyhttp.core.constant.StatusCodes; import com.tvd12.ezyhttp.core.exception.HttpConflictException; import com.tvd12.test.assertion.Asserts; +import org.testng.annotations.Test; public class HttpConflictExceptionTest { @@ -21,4 +20,23 @@ public void test() { Asserts.assertEquals(code, sut.getCode()); Asserts.assertEquals(data, sut.getData()); } + + @Test + public void withCauseTest() { + // given + int code = StatusCodes.CONFLICT; + String data = "error"; + Exception cause = new Exception("test"); + + // when + HttpConflictException sut = new HttpConflictException( + data, + cause + ); + + // then + Asserts.assertEquals(code, sut.getCode()); + Asserts.assertEquals(data, sut.getData()); + Asserts.assertEquals(sut.getCause(), cause); + } } diff --git a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpForbiddenExceptionTest.java b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpForbiddenExceptionTest.java index 90ce23a3..6560fe87 100644 --- a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpForbiddenExceptionTest.java +++ b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpForbiddenExceptionTest.java @@ -1,10 +1,9 @@ package com.tvd12.ezyhttp.core.test.exception; -import org.testng.annotations.Test; - import com.tvd12.ezyhttp.core.constant.StatusCodes; import com.tvd12.ezyhttp.core.exception.HttpForbiddenException; import com.tvd12.test.assertion.Asserts; +import org.testng.annotations.Test; public class HttpForbiddenExceptionTest { @@ -21,4 +20,23 @@ public void test() { Asserts.assertEquals(code, sut.getCode()); Asserts.assertEquals(data, sut.getData()); } + + @Test + public void withCauseTest() { + // given + int code = StatusCodes.FORBIDDEN; + String data = "error"; + Exception cause = new Exception("test"); + + // when + HttpForbiddenException sut = new HttpForbiddenException( + data, + cause + ); + + // then + Asserts.assertEquals(code, sut.getCode()); + Asserts.assertEquals(data, sut.getData()); + Asserts.assertEquals(sut.getCause(), cause); + } } diff --git a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpInternalServerErrorExceptionTest.java b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpInternalServerErrorExceptionTest.java index c53c209f..5e8b03e8 100644 --- a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpInternalServerErrorExceptionTest.java +++ b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpInternalServerErrorExceptionTest.java @@ -1,10 +1,9 @@ package com.tvd12.ezyhttp.core.test.exception; -import org.testng.annotations.Test; - import com.tvd12.ezyhttp.core.constant.StatusCodes; import com.tvd12.ezyhttp.core.exception.HttpInternalServerErrorException; import com.tvd12.test.assertion.Asserts; +import org.testng.annotations.Test; public class HttpInternalServerErrorExceptionTest { @@ -21,4 +20,23 @@ public void test() { Asserts.assertEquals(code, sut.getCode()); Asserts.assertEquals(data, sut.getData()); } + + @Test + public void withCauseTest() { + // given + int code = StatusCodes.INTERNAL_SERVER_ERROR; + String data = "error"; + Exception cause = new Exception("test"); + + // when + HttpInternalServerErrorException sut = new HttpInternalServerErrorException( + data, + cause + ); + + // then + Asserts.assertEquals(code, sut.getCode()); + Asserts.assertEquals(data, sut.getData()); + Asserts.assertEquals(sut.getCause(), cause); + } } diff --git a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpMethodNotAllowedExceptionTest.java b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpMethodNotAllowedExceptionTest.java index 54436231..b4d7a893 100644 --- a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpMethodNotAllowedExceptionTest.java +++ b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpMethodNotAllowedExceptionTest.java @@ -1,10 +1,9 @@ package com.tvd12.ezyhttp.core.test.exception; -import org.testng.annotations.Test; - import com.tvd12.ezyhttp.core.constant.StatusCodes; import com.tvd12.ezyhttp.core.exception.HttpMethodNotAllowedException; import com.tvd12.test.assertion.Asserts; +import org.testng.annotations.Test; public class HttpMethodNotAllowedExceptionTest { @@ -21,4 +20,23 @@ public void test() { Asserts.assertEquals(code, sut.getCode()); Asserts.assertEquals(data, sut.getData()); } + + @Test + public void withCauseTest() { + // given + int code = StatusCodes.METHOD_NOT_ALLOWED; + String data = "error"; + Exception cause = new Exception("test"); + + // when + HttpMethodNotAllowedException sut = new HttpMethodNotAllowedException( + data, + cause + ); + + // then + Asserts.assertEquals(code, sut.getCode()); + Asserts.assertEquals(data, sut.getData()); + Asserts.assertEquals(sut.getCause(), cause); + } } diff --git a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpNotAcceptableExceptionTest.java b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpNotAcceptableExceptionTest.java index 66c2556c..9b0a8fb1 100644 --- a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpNotAcceptableExceptionTest.java +++ b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpNotAcceptableExceptionTest.java @@ -1,10 +1,9 @@ package com.tvd12.ezyhttp.core.test.exception; -import org.testng.annotations.Test; - import com.tvd12.ezyhttp.core.constant.StatusCodes; import com.tvd12.ezyhttp.core.exception.HttpNotAcceptableException; import com.tvd12.test.assertion.Asserts; +import org.testng.annotations.Test; public class HttpNotAcceptableExceptionTest { @@ -21,4 +20,23 @@ public void test() { Asserts.assertEquals(code, sut.getCode()); Asserts.assertEquals(data, sut.getData()); } + + @Test + public void withCauseTest() { + // given + int code = StatusCodes.NOT_ACCEPTABLE; + String data = "error"; + Exception cause = new Exception("test"); + + // when + HttpNotAcceptableException sut = new HttpNotAcceptableException( + data, + cause + ); + + // then + Asserts.assertEquals(code, sut.getCode()); + Asserts.assertEquals(data, sut.getData()); + Asserts.assertEquals(sut.getCause(), cause); + } } diff --git a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpNotFoundExceptionTest.java b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpNotFoundExceptionTest.java index e192eb8b..9c8b8ef2 100644 --- a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpNotFoundExceptionTest.java +++ b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpNotFoundExceptionTest.java @@ -1,10 +1,9 @@ package com.tvd12.ezyhttp.core.test.exception; -import org.testng.annotations.Test; - import com.tvd12.ezyhttp.core.constant.StatusCodes; import com.tvd12.ezyhttp.core.exception.HttpNotFoundException; import com.tvd12.test.assertion.Asserts; +import org.testng.annotations.Test; public class HttpNotFoundExceptionTest { @@ -21,4 +20,23 @@ public void test() { Asserts.assertEquals(code, sut.getCode()); Asserts.assertEquals(data, sut.getData()); } + + @Test + public void withCauseTest() { + // given + int code = StatusCodes.NOT_FOUND; + String data = "error"; + Exception cause = new Exception("test"); + + // when + HttpNotFoundException sut = new HttpNotFoundException( + data, + cause + ); + + // then + Asserts.assertEquals(code, sut.getCode()); + Asserts.assertEquals(data, sut.getData()); + Asserts.assertEquals(sut.getCause(), cause); + } } diff --git a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpPaymentRequiredExceptionTest.java b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpPaymentRequiredExceptionTest.java index 404772e6..71340ebe 100644 --- a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpPaymentRequiredExceptionTest.java +++ b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpPaymentRequiredExceptionTest.java @@ -20,4 +20,23 @@ public void test() { Asserts.assertEquals(code, sut.getCode()); Asserts.assertEquals(data, sut.getData()); } + + @Test + public void withCauseTest() { + // given + int code = StatusCodes.PAYMENT_REQUIRED; + String data = "error"; + Exception cause = new Exception("test"); + + // when + HttpPaymentRequiredException sut = new HttpPaymentRequiredException( + data, + cause + ); + + // then + Asserts.assertEquals(code, sut.getCode()); + Asserts.assertEquals(data, sut.getData()); + Asserts.assertEquals(sut.getCause(), cause); + } } diff --git a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpRequestExceptionTest.java b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpRequestExceptionTest.java index 8924ed9d..72342417 100644 --- a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpRequestExceptionTest.java +++ b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpRequestExceptionTest.java @@ -1,10 +1,9 @@ package com.tvd12.ezyhttp.core.test.exception; -import org.testng.annotations.Test; - import com.tvd12.ezyhttp.core.constant.StatusCodes; import com.tvd12.ezyhttp.core.exception.HttpRequestException; import com.tvd12.test.assertion.Asserts; +import org.testng.annotations.Test; public class HttpRequestExceptionTest { @@ -21,4 +20,24 @@ public void test() { Asserts.assertEquals(code, sut.getCode()); Asserts.assertEquals(data, sut.getData()); } + + @Test + public void withCauseTest() { + // given + int code = StatusCodes.BAD_REQUEST; + String data = "error"; + Exception cause = new Exception("test"); + + // when + HttpRequestException sut = new HttpRequestException( + code, + data, + cause + ); + + // then + Asserts.assertEquals(code, sut.getCode()); + Asserts.assertEquals(data, sut.getData()); + Asserts.assertEquals(sut.getCause(), cause); + } } diff --git a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpRequestTimeoutExceptionTest.java b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpRequestTimeoutExceptionTest.java index 20604e55..39d5f08d 100644 --- a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpRequestTimeoutExceptionTest.java +++ b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpRequestTimeoutExceptionTest.java @@ -1,10 +1,9 @@ package com.tvd12.ezyhttp.core.test.exception; -import org.testng.annotations.Test; - import com.tvd12.ezyhttp.core.constant.StatusCodes; import com.tvd12.ezyhttp.core.exception.HttpRequestTimeoutException; import com.tvd12.test.assertion.Asserts; +import org.testng.annotations.Test; public class HttpRequestTimeoutExceptionTest { @@ -21,4 +20,23 @@ public void test() { Asserts.assertEquals(code, sut.getCode()); Asserts.assertEquals(data, sut.getData()); } + + @Test + public void withCauseTest() { + // given + int code = StatusCodes.REQUEST_TIMEOUT; + String data = "error"; + Exception cause = new Exception("test"); + + // when + HttpRequestTimeoutException sut = new HttpRequestTimeoutException( + data, + cause + ); + + // then + Asserts.assertEquals(code, sut.getCode()); + Asserts.assertEquals(data, sut.getData()); + Asserts.assertEquals(sut.getCause(), cause); + } } diff --git a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpTooManyRequestsExceptionTest.java b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpTooManyRequestsExceptionTest.java index d4411190..619c9823 100644 --- a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpTooManyRequestsExceptionTest.java +++ b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpTooManyRequestsExceptionTest.java @@ -20,4 +20,23 @@ public void test() { Asserts.assertEquals(code, sut.getCode()); Asserts.assertEquals(data, sut.getData()); } + + @Test + public void withCauseTest() { + // given + int code = StatusCodes.TOO_MANY_REQUESTS; + String data = "error"; + Exception cause = new Exception("test"); + + // when + HttpTooManyRequestsException sut = new HttpTooManyRequestsException( + data, + cause + ); + + // then + Asserts.assertEquals(code, sut.getCode()); + Asserts.assertEquals(data, sut.getData()); + Asserts.assertEquals(sut.getCause(), cause); + } } diff --git a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpUnauthorizedExceptionTest.java b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpUnauthorizedExceptionTest.java index a946c330..71a88cc7 100644 --- a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpUnauthorizedExceptionTest.java +++ b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpUnauthorizedExceptionTest.java @@ -1,10 +1,9 @@ package com.tvd12.ezyhttp.core.test.exception; -import org.testng.annotations.Test; - import com.tvd12.ezyhttp.core.constant.StatusCodes; import com.tvd12.ezyhttp.core.exception.HttpUnauthorizedException; import com.tvd12.test.assertion.Asserts; +import org.testng.annotations.Test; public class HttpUnauthorizedExceptionTest { @@ -21,4 +20,23 @@ public void test() { Asserts.assertEquals(code, sut.getCode()); Asserts.assertEquals(data, sut.getData()); } + + @Test + public void withCauseTest() { + // given + int code = StatusCodes.UNAUTHORIZED; + String data = "error"; + Exception cause = new Exception("test"); + + // when + HttpUnauthorizedException sut = new HttpUnauthorizedException( + data, + cause + ); + + // then + Asserts.assertEquals(code, sut.getCode()); + Asserts.assertEquals(data, sut.getData()); + Asserts.assertEquals(sut.getCause(), cause); + } } diff --git a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpUnsupportedMediaTypeExceptionTest.java b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpUnsupportedMediaTypeExceptionTest.java index 2e1abffb..7b0a1101 100644 --- a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpUnsupportedMediaTypeExceptionTest.java +++ b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/exception/HttpUnsupportedMediaTypeExceptionTest.java @@ -1,10 +1,9 @@ package com.tvd12.ezyhttp.core.test.exception; -import org.testng.annotations.Test; - import com.tvd12.ezyhttp.core.constant.StatusCodes; import com.tvd12.ezyhttp.core.exception.HttpUnsupportedMediaTypeException; import com.tvd12.test.assertion.Asserts; +import org.testng.annotations.Test; public class HttpUnsupportedMediaTypeExceptionTest { @@ -21,4 +20,23 @@ public void test() { Asserts.assertEquals(code, sut.getCode()); Asserts.assertEquals(data, sut.getData()); } + + @Test + public void withCauseTest() { + // given + int code = StatusCodes.UNSUPPORTED_MEDIA_TYPE; + String data = "error"; + Exception cause = new Exception("test"); + + // when + HttpUnsupportedMediaTypeException sut = new HttpUnsupportedMediaTypeException( + data, + cause + ); + + // then + Asserts.assertEquals(code, sut.getCode()); + Asserts.assertEquals(data, sut.getData()); + Asserts.assertEquals(sut.getCause(), cause); + } } diff --git a/ezyhttp-server-boot/src/test/java/com/tvd12/ezyhttp/server/boot/test/controller/CustomerController.java b/ezyhttp-server-boot/src/test/java/com/tvd12/ezyhttp/server/boot/test/controller/CustomerController.java index 837cb17a..3808dcba 100644 --- a/ezyhttp-server-boot/src/test/java/com/tvd12/ezyhttp/server/boot/test/controller/CustomerController.java +++ b/ezyhttp-server-boot/src/test/java/com/tvd12/ezyhttp/server/boot/test/controller/CustomerController.java @@ -80,7 +80,7 @@ protected void validateCustomer(Customer customer) { if (customer.getAge() < 1) errors.put("age", "invalid"); } - if (errors.size() > 0) + if (!errors.isEmpty()) throw new HttpBadRequestException(errors); } } diff --git a/ezyhttp-server-boot/src/test/java/com/tvd12/ezyhttp/server/boot/test/graphql/GraphQLHeroDataFetcher.java b/ezyhttp-server-boot/src/test/java/com/tvd12/ezyhttp/server/boot/test/graphql/GraphQLHeroDataFetcher.java index 44e34e14..d341e9e2 100644 --- a/ezyhttp-server-boot/src/test/java/com/tvd12/ezyhttp/server/boot/test/graphql/GraphQLHeroDataFetcher.java +++ b/ezyhttp-server-boot/src/test/java/com/tvd12/ezyhttp/server/boot/test/graphql/GraphQLHeroDataFetcher.java @@ -3,13 +3,17 @@ import com.tvd12.ezyfox.bean.annotation.EzySingleton; import com.tvd12.ezyhttp.server.core.request.RequestArguments; import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLAbstractDataFetcher; +import com.tvd12.ezyhttp.server.graphql.query.GraphQLQueryDefinition; @EzySingleton public class GraphQLHeroDataFetcher - extends GraphQLAbstractDataFetcher { + extends GraphQLAbstractDataFetcher { - public int[] getData(RequestArguments argument, Object parameter) { + public int[] getData( + RequestArguments argument, + GraphQLQueryDefinition query + ) { return new int[]{1, 2, 3}; } diff --git a/ezyhttp-server-boot/src/test/java/com/tvd12/ezyhttp/server/boot/test/graphql/GraphQLMeDataFetcher.java b/ezyhttp-server-boot/src/test/java/com/tvd12/ezyhttp/server/boot/test/graphql/GraphQLMeDataFetcher.java index 125b9a97..55423b65 100644 --- a/ezyhttp-server-boot/src/test/java/com/tvd12/ezyhttp/server/boot/test/graphql/GraphQLMeDataFetcher.java +++ b/ezyhttp-server-boot/src/test/java/com/tvd12/ezyhttp/server/boot/test/graphql/GraphQLMeDataFetcher.java @@ -1,29 +1,27 @@ package com.tvd12.ezyhttp.server.boot.test.graphql; -import java.util.Arrays; -import java.util.List; - import com.tvd12.ezyfox.bean.annotation.EzySingleton; -import com.tvd12.ezyhttp.server.boot.test.graphql.GraphQLMeDataFetcher.MeRequest; -import com.tvd12.ezyhttp.server.boot.test.graphql.GraphQLMeDataFetcher.MeResponse; import com.tvd12.ezyhttp.server.core.request.RequestArguments; import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLAbstractDataFetcher; - +import com.tvd12.ezyhttp.server.graphql.query.GraphQLQueryDefinition; import lombok.Builder; import lombok.Data; import lombok.Getter; +import java.util.Arrays; +import java.util.List; + @EzySingleton public class GraphQLMeDataFetcher - extends GraphQLAbstractDataFetcher { + extends GraphQLAbstractDataFetcher { @Override public MeResponse getData( RequestArguments arguments, - MeRequest parameter + GraphQLQueryDefinition query ) { return MeResponse.builder() - .id(1) + .id(query.getArgumentValue("id", long.class)) .name("Dzung") .nickName("Hello") .friends( diff --git a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/config/GraphQLConfiguration.java b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/config/GraphQLConfiguration.java index 1a654fc6..5550fb42 100644 --- a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/config/GraphQLConfiguration.java +++ b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/config/GraphQLConfiguration.java @@ -1,21 +1,22 @@ package com.tvd12.ezyhttp.server.graphql.config; -import java.util.List; - import com.fasterxml.jackson.databind.ObjectMapper; import com.tvd12.ezyfox.annotation.EzyProperty; import com.tvd12.ezyfox.bean.EzyBeanConfig; import com.tvd12.ezyfox.bean.EzySingletonFactory; import com.tvd12.ezyfox.bean.EzySingletonFactoryAware; -import com.tvd12.ezyfox.bean.annotation.EzyAutoBind; import com.tvd12.ezyfox.bean.annotation.EzyConfigurationAfter; import com.tvd12.ezyhttp.server.graphql.controller.GraphQLController; - +import com.tvd12.ezyhttp.server.graphql.data.GraphQLDataFilter; +import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLDataFetcher; import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLDataFetcherManager; import com.tvd12.ezyhttp.server.graphql.interceptor.GraphQLInterceptorManager; +import com.tvd12.ezyhttp.server.graphql.json.GraphQLObjectMapperFactory; import com.tvd12.ezyhttp.server.graphql.scheme.GraphQLSchemaParser; import lombok.Setter; +import java.util.List; + @Setter @EzyConfigurationAfter public class GraphQLConfiguration implements @@ -28,23 +29,28 @@ public class GraphQLConfiguration implements @EzyProperty("graphql.authenticated") private boolean graphQLAuthenticated; - @EzyAutoBind - private ObjectMapper objectMapper; - private EzySingletonFactory singletonFactory; - @SuppressWarnings("rawtypes") + @SuppressWarnings("unchecked") @Override public void config() { if (!graphQLEnable) { return; } - GraphQLSchemaParser schemaParser = new GraphQLSchemaParser(); + GraphQLObjectMapperFactory graphQLObjectMapperFactory = + new GraphQLObjectMapperFactory(); + ObjectMapper objectMapper = graphQLObjectMapperFactory + .newObjectMapper(); + GraphQLSchemaParser schemaParser = new GraphQLSchemaParser( + objectMapper + ); + GraphQLDataFilter dataFilter = new GraphQLDataFilter(); GraphQLDataFetcherManager.Builder dataFetcherManagerBuilder = GraphQLDataFetcherManager.builder(); - List singletons = singletonFactory.getSingletons(); - for (Object singleton : singletons) { - dataFetcherManagerBuilder.addDataFetcher(singleton); + List dataFetchers = singletonFactory + .getSingletonsOf(GraphQLDataFetcher.class); + for (GraphQLDataFetcher dataFetcher : dataFetchers) { + dataFetcherManagerBuilder.addDataFetcher(dataFetcher); } GraphQLDataFetcherManager dataFetcherManager = dataFetcherManagerBuilder .build(); @@ -54,6 +60,7 @@ public void config() { .authenticated(graphQLAuthenticated) .objectMapper(objectMapper) .schemaParser(schemaParser) + .dataFilter(dataFilter) .dataFetcherManager(dataFetcherManager) .interceptorManager(interceptorManager) .build(); diff --git a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/constants/GraphQLConstants.java b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/constants/GraphQLConstants.java index b3fc28d3..8c51215b 100644 --- a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/constants/GraphQLConstants.java +++ b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/constants/GraphQLConstants.java @@ -6,5 +6,7 @@ public final class GraphQLConstants { public static final String DEFAULT_QL_GROUP_NAME = "default"; + public static final String PREFIX_QUERY = "query"; + private GraphQLConstants() {} } diff --git a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/controller/GraphQLController.java b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/controller/GraphQLController.java index 5d5d9948..a804ed56 100644 --- a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/controller/GraphQLController.java +++ b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/controller/GraphQLController.java @@ -9,9 +9,9 @@ import com.tvd12.ezyhttp.server.core.handler.AuthenticatedController; import com.tvd12.ezyhttp.server.core.handler.IRequestController; import com.tvd12.ezyhttp.server.core.request.RequestArguments; +import com.tvd12.ezyhttp.server.graphql.data.GraphQLDataFilter; import com.tvd12.ezyhttp.server.graphql.data.GraphQLField; import com.tvd12.ezyhttp.server.graphql.data.GraphQLRequest; -import com.tvd12.ezyhttp.server.graphql.exception.GraphQLInvalidSchemeException; import com.tvd12.ezyhttp.server.graphql.exception.GraphQLObjectMapperException; import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLDataFetcher; import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLDataFetcherManager; @@ -22,9 +22,13 @@ import com.tvd12.ezyhttp.server.graphql.scheme.GraphQLSchemaParser; import lombok.Getter; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; -import static com.tvd12.ezyhttp.server.graphql.constants.GraphQLConstants.ALL_FIELDS; +import static com.tvd12.ezyfox.io.EzyStrings.isNotBlank; +import static java.util.Collections.singletonMap; @Api @Authenticatable @@ -35,6 +39,7 @@ public class GraphQLController private final boolean authenticated; private final ObjectMapper objectMapper; private final GraphQLSchemaParser schemaParser; + private final GraphQLDataFilter dataFilter; private final GraphQLDataFetcherManager dataFetcherManager; private final GraphQLInterceptorManager interceptorManager; @@ -42,6 +47,7 @@ public GraphQLController(Builder builder) { this.authenticated = builder.authenticated; this.objectMapper = builder.objectMapper; this.schemaParser = builder.schemaParser; + this.dataFilter = builder.dataFilter; this.dataFetcherManager = builder.dataFetcherManager; this.interceptorManager = builder.interceptorManager; } @@ -50,21 +56,35 @@ public GraphQLController(Builder builder) { * Follow by this suggestion: https://graphql.org/learn/serving-over-http/. * Example: * - * curl --location -g --request GET 'http://localhost:8083/graphql?query={me{id+name+friends{name}}}&variables={"id" : 1}' + * curl --location -g --request GET 'http://localhost:8083/graphql?query={me{myVariable: $someValue}{id+name+friends{name}}}&variables={"id" : 1}' * * * @param query GraphQL query * @param variables a JSON-encoded string like { "myVariable": "someValue", ... } * @return the result - * @throws Exception when have any error */ + @SuppressWarnings("unchecked") @DoGet("/graphql") public Object doGet( RequestArguments arguments, @RequestParam("query") String query, @RequestParam("variables") String variables - ) throws Exception { - return fetch(arguments, query, variables); + ) { + Map variableMap = Collections.emptyMap(); + if (isNotBlank(variables)) { + try { + variableMap = objectMapper.readValue( + variables, + Map.class + ); + } catch (Exception e) { + throw new GraphQLObjectMapperException( + singletonMap("variables", "invalid"), + e + ); + } + } + return fetch(arguments, query, variableMap); } /** @@ -74,20 +94,19 @@ public Object doGet( * curl --location --request POST 'http://localhost:8083/graphql' \ * --header 'Content-Type: application/json' \ * --data-raw '{ - * "query": "{me{id+name+friends{name}}}", + * "query": "{me(id: $id){id+name+friends{name}}}", * "variables": {"id" : 1} * }' * * * @param request the request body * @return the result - * @throws Exception when have any error */ @DoPost("/graphql") public Object doPost( RequestArguments arguments, @RequestBody GraphQLRequest request - ) throws Exception { + ) { return fetch( arguments, request.getQuery(), @@ -99,42 +118,33 @@ public Object doPost( private Object fetch( RequestArguments arguments, String query, - Object variables - ) throws Exception { - GraphQLSchema schema = schemaParser.parseQuery(query); - List queryDefinitions = schema.getQueryDefinitions(); + Map variables + ) { + GraphQLSchema schema = schemaParser.parseQuery(query, variables); + List queryDefinitions = schema + .getQueryDefinitions(); Map answer = new HashMap<>(); for (GraphQLQueryDefinition queryDefinition : queryDefinitions) { String queryName = queryDefinition.getName(); - GraphQLDataFetcher dataFetcher = dataFetcherManager.getDataFetcher(queryName); + GraphQLDataFetcher dataFetcher = dataFetcherManager + .getDataFetcher(queryName); if (dataFetcher == null) { throw new HttpNotFoundException( "not found data fetcher with queryName: " + queryName ); } - Class parameterType = dataFetcher.getParameterType(); - Object parameter = variables; - if (parameterType != null) { - if (variables instanceof String) { - parameter = objectMapper.readValue((String) variables, parameterType); - } else { - parameter = objectMapper.convertValue(variables, parameterType); - } - } else { - if (variables instanceof String) { - parameter = objectMapper.readValue((String) variables, Map.class); - } - } List interceptors = interceptorManager .getRequestInterceptors(); - String queryGroup = dataFetcherManager.getGroupNameByQueryName(queryName); + String queryGroup = dataFetcherManager.getGroupNameByQueryName( + queryName + ); for (GraphQLInterceptor interceptor : interceptors) { boolean ok = interceptor.preHandle( arguments, queryGroup, queryName, - parameter, + queryDefinition, dataFetcher ); if (!ok) { @@ -149,10 +159,13 @@ private Object fetch( } Object data = dataFetcher.getData( arguments, - parameter + queryDefinition ); try { - Object currentResponse = mapToResponse(data, queryDefinition, query); + Object currentResponse = mapToResponse( + data, + queryDefinition + ); answer.put(queryName, currentResponse); } catch (GraphQLObjectMapperException e) { answer.put(queryName, data); @@ -162,6 +175,7 @@ private Object fetch( arguments, queryGroup, queryName, + queryDefinition, answer, dataFetcher ); @@ -173,79 +187,25 @@ private Object fetch( @SuppressWarnings({"rawtypes"}) private Map mapToResponse( Object data, - GraphQLQueryDefinition queryDefinition, - String query + GraphQLQueryDefinition queryDefinition ) { Map dataMap; try { dataMap = objectMapper.convertValue(data, Map.class); } catch (Exception e) { throw new GraphQLObjectMapperException( - "Could not convert: " + data.getClass() + " to Map" + singletonMap("response", "invalid") ); } - return filterDataMap(dataMap, queryDefinition, query); + return filterDataMap(dataMap, queryDefinition); } - @SuppressWarnings({"unchecked", "rawtypes"}) + @SuppressWarnings("rawtypes") private Map filterDataMap( Map dataMap, - GraphQLField queryDefinition, - String query + GraphQLField queryDefinition ) { - Map answer = new HashMap<>(); - Map parentMap = null; - Stack stack = new Stack<>(); - stack.add(queryDefinition); - while (!stack.isEmpty()) { - GraphQLField parent = stack.pop(); - parentMap = parentMap == null - ? answer - : (Map) parentMap.get(parent.getName()); - - for (GraphQLField field : parent.getFields()) { - String fieldName = field.getName(); - if (fieldName.equals(ALL_FIELDS)) { - parentMap.putAll(dataMap); - continue; - } - Object value = dataMap.get(fieldName); - if (value == null) { - continue; - } - if (field.getFields().isEmpty()) { - parentMap.put(fieldName, value); - continue; - } - if (value instanceof Map) { - Object newItem = new HashMap<>(); - parentMap.put(fieldName, newItem); - stack.push(field); - } else if (value instanceof List) { - parentMap.put( - fieldName, - filterDataList((List) value, field, query) - ); - } else { - throw new GraphQLInvalidSchemeException( - "invalid schema: " + query + " at: " + fieldName - ); - } - } - } - return answer; - } - - @SuppressWarnings({"rawtypes"}) - private List filterDataList( - List dataList, - GraphQLField queryDefinition, String query - ) { - List answer = new LinkedList<>(); - for (Map map : dataList) { - answer.add(filterDataMap(map, queryDefinition, query)); - } - return answer; + return dataFilter.filter(dataMap, queryDefinition); } public static Builder builder() { @@ -256,6 +216,7 @@ public static class Builder implements EzyBuilder { private boolean authenticated; private ObjectMapper objectMapper; private GraphQLSchemaParser schemaParser; + private GraphQLDataFilter dataFilter; private GraphQLDataFetcherManager dataFetcherManager; private GraphQLInterceptorManager interceptorManager; @@ -274,12 +235,21 @@ public Builder schemaParser(GraphQLSchemaParser schemaParser) { return this; } - public Builder dataFetcherManager(GraphQLDataFetcherManager dataFetcherManager) { + public Builder dataFilter(GraphQLDataFilter dataFilter) { + this.dataFilter = dataFilter; + return this; + } + + public Builder dataFetcherManager( + GraphQLDataFetcherManager dataFetcherManager + ) { this.dataFetcherManager = dataFetcherManager; return this; } - public Builder interceptorManager(GraphQLInterceptorManager interceptorManager) { + public Builder interceptorManager( + GraphQLInterceptorManager interceptorManager + ) { this.interceptorManager = interceptorManager; return this; } diff --git a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/data/GraphQLDataFilter.java b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/data/GraphQLDataFilter.java new file mode 100644 index 00000000..fb80573d --- /dev/null +++ b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/data/GraphQLDataFilter.java @@ -0,0 +1,88 @@ +package com.tvd12.ezyhttp.server.graphql.data; + +import com.tvd12.ezyhttp.server.graphql.exception.GraphQLInvalidSchemeException; +import lombok.AllArgsConstructor; + +import java.util.*; + +import static com.tvd12.ezyhttp.server.graphql.constants.GraphQLConstants.ALL_FIELDS; +import static java.util.Collections.singletonMap; + +public class GraphQLDataFilter { + + @SuppressWarnings({"rawtypes", "unchecked"}) + public Map filter( + Map data, + GraphQLField queryDefinition + ) { + Map answer = new HashMap<>(); + Map parentMap = null; + Stack stack = new Stack<>(); + stack.add(new StackEntry(queryDefinition, data)); + while (!stack.isEmpty()) { + StackEntry entry = stack.pop(); + String parentName = entry.field.getName(); + parentMap = parentMap == null + ? answer + : (Map) parentMap.get(parentName); + + GraphQLField allField = entry.field.getField(ALL_FIELDS); + if (allField != null) { + Set entries = entry.data.entrySet(); + for (Map.Entry e : entries) { + Object v = e.getValue(); + if (v != null) { + parentMap.put(e.getKey(), v); + } + } + } + + for (GraphQLField field : entry.field.getFields()) { + String fieldName = field.getName(); + Object value = entry.data.get(fieldName); + if (value == null) { + continue; + } + if (field.getFields().isEmpty()) { + parentMap.put(fieldName, value); + continue; + } + if (value instanceof Map) { + Object newItem = new HashMap<>(); + parentMap.put(fieldName, newItem); + stack.push(new StackEntry(field, (Map) value)); + } else if (value instanceof List) { + parentMap.put( + fieldName, + filterList((List) value, field) + ); + } else { + throw new GraphQLInvalidSchemeException( + singletonMap("schema", "invalid"), + "invalid schema at field: " + fieldName + ); + } + } + } + return answer; + } + + @SuppressWarnings({"rawtypes"}) + public List filterList( + List dataList, + GraphQLField queryDefinition + ) { + List answer = new LinkedList<>(); + for (Map map : dataList) { + answer.add(filter(map, queryDefinition)); + } + return answer; + } + + @AllArgsConstructor + @SuppressWarnings("rawtypes") + private static class StackEntry { + private GraphQLField field; + private Map data; + } +} diff --git a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/data/GraphQLField.java b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/data/GraphQLField.java index c58d8020..5564e610 100644 --- a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/data/GraphQLField.java +++ b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/data/GraphQLField.java @@ -1,28 +1,179 @@ package com.tvd12.ezyhttp.server.graphql.data; import com.tvd12.ezyfox.builder.EzyBuilder; +import com.tvd12.ezyfox.io.EzySingletonOutputTransformer; +import com.tvd12.ezyhttp.core.codec.SingletonStringDeserializer; import lombok.Getter; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; + +import static com.tvd12.ezyfox.io.EzyMaps.newHashMap; @Getter public class GraphQLField { protected final String name; + protected final Map arguments; protected final List fields; + protected final Map fieldByName; protected GraphQLField(Builder builder) { this.name = builder.name; + this.arguments = builder.arguments; this.fields = builder.fields != null ? builder.fields : Collections.emptyList(); + this.fieldByName = newHashMap( + fields, + GraphQLField::getName + ); + } + + public GraphQLField getField(String fieldName) { + return fieldByName.get(fieldName); + } + + /** + * Get argument value by name. + * + * @param argumentName the argument name. + * @return the argument value. + * @param the value type. + */ + @SuppressWarnings("unchecked") + public T getArgumentValue( + String argumentName + ) { + return arguments != null + ? (T) arguments.get(argumentName) + : null; + } + + /** + * Get argument value by name and transforms it into the desired type. + * + * @param argumentName the argument name. + * @param type the argument type class. + * @return the argument value. + * @param the argument type. + */ + @SuppressWarnings("unchecked") + public T getArgumentValue( + String argumentName, + Class type + ) { + Object value = getArgumentValue(argumentName); + if (value == null) { + return null; + } + if (value instanceof String) { + return SingletonStringDeserializer + .getInstance() + .deserializeOrNull((String) value, type); + } + return (T) EzySingletonOutputTransformer + .getInstance() + .transform(value, type); + } + + /** + * Retrieves the value of a specified argument from a nested GraphQL field path. + * + * @param argumentName the name of the argument to retrieve. + * @param fieldNames the sequence of nested field names to traverse in order to + * reach the target field. + * @param the expected return type of the argument value. + * @return the value of the specified argument if found; otherwise, null. + * + *

+ * How it works: + *

    + *
  • Starts from the current field ({@code this}).
  • + *
  • Iteratively navigates through the field names provided in {@code fieldNames}.
  • + *
  • If all fields in the path exist, retrieves the argument value + * from the final field.
  • + *
  • Returns {@code null} if any field in the path is missing.
  • + *
+ *

+ */ + public T getFieldArgumentValue( + String argumentName, + String... fieldNames + ) { + GraphQLField field = this; + for (String fieldName : fieldNames) { + field = fieldByName.get(fieldName); + if (field == null) { + break; + } + } + return field != null + ? field.getArgumentValue(argumentName) + : null; + } + + /** + * Retrieves the value of a specified argument from a nested GraphQL field path + * and transforms it into the desired type. + * + * @param argumentName the name of the argument to retrieve. + * @param type the target class type to which the argument value should be converted. + * @param fieldNames the sequence of nested field names to traverse in order to reach + * the target field. + * @param the expected return type. + * @return the transformed value of the specified argument if found and convertible; + * otherwise, null. + *

+ * How it works: + * - Delegates to {@link #getFieldArgumentValue(String, String...)} to retrieve + * the raw argument value. + * - If the value is non-null, uses {@code EzySingletonOutputTransformer} to transform + * it into the specified type. + * - Returns null if the argument is not found or the value is null. + *

+ * @throws ClassCastException if the transformation result cannot be cast + * to the specified type. + */ + @SuppressWarnings("unchecked") + public T getFieldArgumentValue( + String argumentName, + Class type, + String... fieldNames + ) { + Object value = getFieldArgumentValue( + argumentName, + fieldNames + ); + if (value == null) { + return null; + } + return (T) EzySingletonOutputTransformer + .getInstance() + .transform(value, type); } @Override public String toString() { - return name + ", " + fields; + return toString(name, arguments, fields); + } + + private static String toString( + String name, + Map arguments, + List fields + ) { + StringBuilder builder = new StringBuilder() + .append(name); + if (arguments != null && !arguments.isEmpty()) { + builder.append("(").append(arguments).append(")"); + } + if (fields != null) { + builder.append(", ").append(fields); + } + return builder.toString(); } @Override @@ -47,6 +198,7 @@ public static Builder builder() { public static class Builder implements EzyBuilder { private String name; + private Map arguments; private List fields; public Builder name(String name) { @@ -54,6 +206,11 @@ public Builder name(String name) { return this; } + public Builder arguments(Map arguments) { + this.arguments = arguments; + return this; + } + public Builder addField(GraphQLField field) { if (fields == null) { fields = new ArrayList<>(); @@ -66,5 +223,10 @@ public Builder addField(GraphQLField field) { public GraphQLField build() { return new GraphQLField(this); } + + @Override + public String toString() { + return GraphQLField.toString(name, arguments, fields); + } } } diff --git a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/data/GraphQLRequest.java b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/data/GraphQLRequest.java index 653aaa27..c8869fa6 100644 --- a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/data/GraphQLRequest.java +++ b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/data/GraphQLRequest.java @@ -3,9 +3,18 @@ import lombok.Getter; import lombok.Setter; +import java.util.Collections; +import java.util.Map; + @Getter @Setter public class GraphQLRequest { private String query; - private Object variables; + private Map variables; + + public Map getVariables() { + return variables != null + ? variables + : Collections.emptyMap(); + } } diff --git a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/exception/GraphQLInvalidSchemeException.java b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/exception/GraphQLInvalidSchemeException.java index 1fb32019..246acf9b 100644 --- a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/exception/GraphQLInvalidSchemeException.java +++ b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/exception/GraphQLInvalidSchemeException.java @@ -1,9 +1,24 @@ package com.tvd12.ezyhttp.server.graphql.exception; +import lombok.Getter; + +@Getter public class GraphQLInvalidSchemeException extends IllegalArgumentException { private static final long serialVersionUID = 1908055626427476066L; - public GraphQLInvalidSchemeException(String s) { - super(s); + private final Object errors; + + public GraphQLInvalidSchemeException( + Object errors + ) { + this(errors, errors.toString()); + } + + public GraphQLInvalidSchemeException( + Object errors, + String message + ) { + super(message); + this.errors = errors; } } diff --git a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/exception/GraphQLObjectMapperException.java b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/exception/GraphQLObjectMapperException.java index 06a2fecd..806e85c5 100644 --- a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/exception/GraphQLObjectMapperException.java +++ b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/exception/GraphQLObjectMapperException.java @@ -1,9 +1,40 @@ package com.tvd12.ezyhttp.server.graphql.exception; +import lombok.Getter; + +@Getter public class GraphQLObjectMapperException extends IllegalArgumentException { private static final long serialVersionUID = 3508582611517214186L; - public GraphQLObjectMapperException(String s) { - super(s); + private final Object errors; + + public GraphQLObjectMapperException( + Object errors + ) { + this(errors, errors.toString()); + } + + public GraphQLObjectMapperException( + Object errors, + String message + ) { + super(message); + this.errors = errors; + } + + public GraphQLObjectMapperException( + Object errors, + Throwable cause + ) { + this(errors, errors.toString(), cause); + } + + public GraphQLObjectMapperException( + Object errors, + String message, + Throwable cause + ) { + super(message, cause); + this.errors = errors; } } diff --git a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/fetcher/GraphQLAbstractDataFetcher.java b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/fetcher/GraphQLAbstractDataFetcher.java index d30edd15..765417bd 100644 --- a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/fetcher/GraphQLAbstractDataFetcher.java +++ b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/fetcher/GraphQLAbstractDataFetcher.java @@ -1,22 +1,7 @@ package com.tvd12.ezyhttp.server.graphql.fetcher; -import java.lang.reflect.Type; - -import com.tvd12.ezyfox.reflect.EzyGenerics; import com.tvd12.ezyfox.util.EzyLoggable; -public abstract class GraphQLAbstractDataFetcher +public abstract class GraphQLAbstractDataFetcher extends EzyLoggable - implements GraphQLDataFetcher { - - @Override - public Class getParameterType() { - try { - Type genericSuperclass = getClass().getGenericSuperclass(); - Class[] args = EzyGenerics.getTwoGenericClassArguments(genericSuperclass); - return args[0]; - } catch (Exception e) { - return null; - } - } -} + implements GraphQLDataFetcher {} diff --git a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/fetcher/GraphQLDataFetcher.java b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/fetcher/GraphQLDataFetcher.java index 0def9d65..18114a7b 100644 --- a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/fetcher/GraphQLDataFetcher.java +++ b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/fetcher/GraphQLDataFetcher.java @@ -1,16 +1,18 @@ package com.tvd12.ezyhttp.server.graphql.fetcher; import com.tvd12.ezyfox.exception.EzyNotImplementedException; -import com.tvd12.ezyfox.reflect.EzyGenerics; import com.tvd12.ezyhttp.server.core.request.RequestArguments; import com.tvd12.ezyhttp.server.graphql.annotation.GraphQLQuery; +import com.tvd12.ezyhttp.server.graphql.query.GraphQLQueryDefinition; import com.tvd12.ezyhttp.server.graphql.scheme.GraphQLDataSchema; -public interface GraphQLDataFetcher { +import java.util.List; - D getData( +public interface GraphQLDataFetcher { + + Object getData( RequestArguments arguments, - A parameter + GraphQLQueryDefinition query ); default String getQueryName() { @@ -23,21 +25,7 @@ default String getQueryName() { ); } - default Class getParameterType() { - try { - Class readerClass = getClass(); - Class[] args = EzyGenerics.getGenericInterfacesArguments( - readerClass, - GraphQLDataFetcher.class, - 2 - ); - return args[0]; - } catch (Exception e) { - return null; - } - } - - default GraphQLDataSchema getRequestScheme() { + default List getQueryScheme() { return null; } diff --git a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/fetcher/GraphQLDataFetcherManager.java b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/fetcher/GraphQLDataFetcherManager.java index f4a05ead..8727c183 100644 --- a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/fetcher/GraphQLDataFetcherManager.java +++ b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/fetcher/GraphQLDataFetcherManager.java @@ -7,7 +7,6 @@ import static com.tvd12.ezyhttp.server.graphql.util.GraphQLQueryGroupExtractors.extractQueryGroup; -@SuppressWarnings("rawtypes") public class GraphQLDataFetcherManager { private final Map dataFetchers; @@ -61,12 +60,8 @@ public static class Builder implements EzyBuilder { private final Map> queryNamesByGroupName = new HashMap<>(); - public Builder addDataFetcher(Object fetcher) { - if (fetcher instanceof GraphQLDataFetcher) { - GraphQLDataFetcher f = (GraphQLDataFetcher) fetcher; - return addDataFetcher(f.getQueryName(), f); - } - return this; + public Builder addDataFetcher(GraphQLDataFetcher fetcher) { + return addDataFetcher(fetcher.getQueryName(), fetcher); } public Builder addDataFetcher( diff --git a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/interceptor/GraphQLInterceptor.java b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/interceptor/GraphQLInterceptor.java index ac54778f..0c83dfd9 100644 --- a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/interceptor/GraphQLInterceptor.java +++ b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/interceptor/GraphQLInterceptor.java @@ -2,6 +2,7 @@ import com.tvd12.ezyhttp.server.core.request.RequestArguments; import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLDataFetcher; +import com.tvd12.ezyhttp.server.graphql.query.GraphQLQueryDefinition; public interface GraphQLInterceptor { @@ -9,8 +10,8 @@ default boolean preHandle( RequestArguments arguments, String queryGroup, String queryName, - Object requestParameter, - GraphQLDataFetcher dataFetcher + GraphQLQueryDefinition queryDefinition, + GraphQLDataFetcher dataFetcher ) { return true; } @@ -19,8 +20,9 @@ default void postHandle( RequestArguments arguments, String queryGroup, String queryName, + GraphQLQueryDefinition queryDefinition, Object responseData, - GraphQLDataFetcher dataFetcher + GraphQLDataFetcher dataFetcher ) {} default int getPriority() { diff --git a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/json/GraphQLObjectMapperFactory.java b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/json/GraphQLObjectMapperFactory.java new file mode 100644 index 00000000..6a6b380c --- /dev/null +++ b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/json/GraphQLObjectMapperFactory.java @@ -0,0 +1,17 @@ +package com.tvd12.ezyhttp.server.graphql.json; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.tvd12.ezyhttp.core.json.ObjectMapperBuilder; + +import static com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_SINGLE_QUOTES; +import static com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES; + +public class GraphQLObjectMapperFactory { + + public ObjectMapper newObjectMapper() { + ObjectMapper objectMapper = new ObjectMapperBuilder().build(); + objectMapper.configure(ALLOW_UNQUOTED_FIELD_NAMES, true); + objectMapper.configure(ALLOW_SINGLE_QUOTES, true); + return objectMapper; + } +} diff --git a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/query/GraphQLQueryDefinition.java b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/query/GraphQLQueryDefinition.java index 7e820f0a..416dc044 100644 --- a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/query/GraphQLQueryDefinition.java +++ b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/query/GraphQLQueryDefinition.java @@ -8,13 +8,6 @@ protected GraphQLQueryDefinition(Builder builder) { super(builder); } - public String getName() { - if (this.name == null) { - throw new IllegalArgumentException("Must provide queryName!"); - } - return this.name; - } - public static Builder builder() { return new Builder(); } diff --git a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/scheme/GraphQLDataSchema.java b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/scheme/GraphQLDataSchema.java index 942709b8..64adb5ea 100644 --- a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/scheme/GraphQLDataSchema.java +++ b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/scheme/GraphQLDataSchema.java @@ -13,6 +13,7 @@ public class GraphQLDataSchema { protected final String type; protected final String format; protected final String description; + protected final List arguments; protected final GraphQLDataSchema items; protected final List properties; protected final Object examples; @@ -22,6 +23,7 @@ protected GraphQLDataSchema(Builder builder) { this.type = builder.type; this.format = builder.format; this.description = builder.description; + this.arguments = builder.arguments; this.items = builder.items; this.properties = builder.properties; this.examples = builder.examples; @@ -36,6 +38,7 @@ public static class Builder implements EzyBuilder { protected String type; protected String format; protected String description; + protected List arguments; protected GraphQLDataSchema items; protected List properties; protected Object examples; @@ -60,6 +63,11 @@ public Builder description(String description) { return this; } + public Builder arguments(List arguments) { + this.arguments = arguments; + return this; + } + public Builder items(GraphQLDataSchema items) { this.items = items; return this; diff --git a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/scheme/GraphQLSchemaParser.java b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/scheme/GraphQLSchemaParser.java index b29f21a2..57cde3ec 100644 --- a/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/scheme/GraphQLSchemaParser.java +++ b/ezyhttp-server-graphql/src/main/java/com/tvd12/ezyhttp/server/graphql/scheme/GraphQLSchemaParser.java @@ -1,22 +1,36 @@ package com.tvd12.ezyhttp.server.graphql.scheme; +import com.fasterxml.jackson.databind.ObjectMapper; import com.tvd12.ezyhttp.server.graphql.data.GraphQLField; +import com.tvd12.ezyhttp.server.graphql.exception.GraphQLObjectMapperException; import com.tvd12.ezyhttp.server.graphql.query.GraphQLQueryDefinition; +import lombok.AllArgsConstructor; +import java.util.Map; import java.util.Stack; +import static com.tvd12.ezyfox.io.EzyStrings.EMPTY_STRING; +import static java.util.Collections.singletonMap; + +@AllArgsConstructor public final class GraphQLSchemaParser { - @SuppressWarnings("MethodLength") - public GraphQLSchema parseQuery(String queryToParse) { + private final ObjectMapper objectMapper; + + @SuppressWarnings({"unchecked", "MethodLength"}) + public GraphQLSchema parseQuery( + String queryToParse, + Map variables + ) { String query = standardize(queryToParse); Stack stack = new Stack<>(); GraphQLSchema.Builder schemaBuilder = GraphQLSchema.builder(); + int queryLength = query.length(); int nameLength = 0; char[] nameBuffer = new char[128]; - for (int i = 0; i < query.length(); ++i) { + for (int i = 0; i < queryLength; ++i) { char ch = query.charAt(i); if (ch == '{') { if (stack.isEmpty()) { @@ -36,6 +50,34 @@ public GraphQLSchema parseQuery(String queryToParse) { continue; } + if (ch == '(') { + GraphQLField.Builder childBuilder = stack.peek(); + StringBuilder argumentsBuilder = new StringBuilder(); + i = extractQueryArguments( + argumentsBuilder, + query, + i, + queryLength, + variables + ); + String arguments = "{" + argumentsBuilder + "}"; + try { + childBuilder.arguments( + objectMapper.readValue( + arguments, + Map.class + ) + ); + } catch (Exception e) { + throw new GraphQLObjectMapperException( + singletonMap("arguments", "invalid"), + "invalid arguments json: " + arguments, + e + ); + } + continue; + } + if (ch == '}') { if (stack.isEmpty()) { continue; @@ -43,16 +85,22 @@ public GraphQLSchema parseQuery(String queryToParse) { if (stack.size() == 1) { GraphQLField.Builder item = stack.pop(); if (nameLength > 0) { - item.name(String.copyValueOf(nameBuffer, 0, nameLength)); + item.name( + String.copyValueOf(nameBuffer, 0, nameLength) + ); nameLength = 0; } - schemaBuilder.addQueryDefinition((GraphQLQueryDefinition) item.build()); + schemaBuilder.addQueryDefinition( + (GraphQLQueryDefinition) item.build() + ); continue; } GraphQLField.Builder childBuilder = stack.pop(); if (nameLength > 0) { - childBuilder.name(String.copyValueOf(nameBuffer, 0, nameLength)); + childBuilder.name( + String.copyValueOf(nameBuffer, 0, nameLength) + ); nameLength = 0; } @@ -61,11 +109,14 @@ public GraphQLSchema parseQuery(String queryToParse) { if (stack.size() == 1) { GraphQLField.Builder item = stack.pop(); - schemaBuilder.addQueryDefinition((GraphQLQueryDefinition) item.build()); + schemaBuilder.addQueryDefinition( + (GraphQLQueryDefinition) item.build() + ); } } else if (ch == ' ') { // ',' '\t' '\n' '+' have been removed if (stack.isEmpty()) { - GraphQLQueryDefinition.Builder queryBuilder = GraphQLQueryDefinition.builder(); + GraphQLQueryDefinition.Builder queryBuilder = + GraphQLQueryDefinition.builder(); stack.add(queryBuilder); nameLength = 0; continue; @@ -75,16 +126,21 @@ public GraphQLSchema parseQuery(String queryToParse) { GraphQLField.Builder item = stack.pop(); item.name(String.copyValueOf(nameBuffer, 0, nameLength)); nameLength = 0; - schemaBuilder.addQueryDefinition((GraphQLQueryDefinition) item.build()); + schemaBuilder.addQueryDefinition( + (GraphQLQueryDefinition) item.build() + ); - GraphQLQueryDefinition.Builder queryBuilder = GraphQLQueryDefinition.builder(); + GraphQLQueryDefinition.Builder queryBuilder = + GraphQLQueryDefinition.builder(); stack.add(queryBuilder); continue; } GraphQLField.Builder childBuilder = stack.pop(); if (nameLength > 0) { - childBuilder.name(String.copyValueOf(nameBuffer, 0, nameLength)); + childBuilder.name( + String.copyValueOf(nameBuffer, 0, nameLength) + ); nameLength = 0; } @@ -108,18 +164,36 @@ public GraphQLSchema parseQuery(String queryToParse) { */ private String standardize(String query) { if (query == null) { - return ""; + return EMPTY_STRING; } String trimedQuery = query.trim(); StringBuilder forwardStandard = forwardStandardize(trimedQuery); - StringBuilder backwardStandard = backwardStandardize(forwardStandard.toString()); + StringBuilder backwardStandard = backwardStandardize( + forwardStandard.toString() + ); return removeQueryPrefix(backwardStandard.toString()); } private StringBuilder forwardStandardize(String query) { + int queryLength = query.length(); StringBuilder answer = new StringBuilder(); - for (int i = 0; i < query.length(); ++i) { + for (int i = 0; i < queryLength; ++i) { char ch = query.charAt(i); + if (ch == '(') { + StringBuilder argumentsBuilder = new StringBuilder(); + i = extractQueryArguments( + argumentsBuilder, + query, + i, + queryLength, + null + ); + answer + .append('(') + .append(argumentsBuilder) + .append(')'); + continue; + } if (ch == '{' || ch == '}') { answer.append(ch); } else if (ch == '+' || ch == ',' || ch == ' ' || ch == '\t' || ch == '\n') { @@ -135,8 +209,9 @@ private StringBuilder forwardStandardize(String query) { } private StringBuilder backwardStandardize(String query) { + int queryLength = query.length(); StringBuilder answer = new StringBuilder(); - for (int i = query.length() - 1; i >= 0; --i) { + for (int i = queryLength - 1; i >= 0; --i) { char ch = query.charAt(i); if (ch == '{' || ch == '}') { answer.insert(0, ch); @@ -159,4 +234,50 @@ private String removeQueryPrefix(String s) { } return s; } + + private int extractQueryArguments( + StringBuilder argumentsBuilder, + String query, + int start, + int queryLength, + Map variables + ) { + int i = start + 1; + int quoteCount = 0; + int quotesCount = 0; + for (; i < queryLength; ++i) { + char prevCh = query.charAt(i - 1); + char ch = query.charAt(i); + if (prevCh != '\\' && ch == '\'') { + quoteCount = quoteCount == 0 ? 1 : 0; + } else if (prevCh != '\\' && ch == '"') { + quotesCount = quotesCount == 0 ? 1 : 0; + } else if (ch == ')' && quoteCount == 0 && quotesCount == 0) { + break; + } else if (variables != null && ch == '$') { + StringBuilder varNameBuilder = new StringBuilder(); + for (++i; i < queryLength; ++i) { + ch = query.charAt(i); + if (ch == ' ') { + continue; + } + if (ch != ',' && ch != ')' && ch != '}') { + varNameBuilder.append(ch); + } else { + --i; + break; + } + } + String varName = varNameBuilder.toString(); + Object value = variables.get(varName); + if (value instanceof String) { + value = "\"" + value + "\""; + } + argumentsBuilder.append(value); + continue; + } + argumentsBuilder.append(ch); + } + return i; + } } diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/config/GraphQLADataFetcher.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/config/GraphQLADataFetcher.java index f0f29ed7..9a754210 100644 --- a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/config/GraphQLADataFetcher.java +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/config/GraphQLADataFetcher.java @@ -2,17 +2,17 @@ import com.tvd12.ezyfox.bean.annotation.EzySingleton; import com.tvd12.ezyhttp.server.core.request.RequestArguments; -import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLDataFetcher; import com.tvd12.ezyhttp.server.graphql.annotation.GraphQLQuery; +import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLDataFetcher; +import com.tvd12.ezyhttp.server.graphql.query.GraphQLQueryDefinition; -@SuppressWarnings("rawtypes") @EzySingleton @GraphQLQuery(name = "A") public class GraphQLADataFetcher implements GraphQLDataFetcher { @Override public Object getData( RequestArguments arguments, - Object parameter + GraphQLQueryDefinition query ) { return "A"; } diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/config/GraphQLConfigurationTest.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/config/GraphQLConfigurationTest.java index 878de741..db92f133 100644 --- a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/config/GraphQLConfigurationTest.java +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/config/GraphQLConfigurationTest.java @@ -1,6 +1,5 @@ package com.tvd12.ezyhttp.server.graphql.test.config; -import com.fasterxml.jackson.databind.ObjectMapper; import com.tvd12.ezyfox.bean.EzyBeanContext; import com.tvd12.ezyfox.bean.EzyBeanContextBuilder; import com.tvd12.ezyfox.bean.EzySingletonFactory; @@ -20,7 +19,7 @@ public class GraphQLConfigurationTest { @Test - @SuppressWarnings({"rawtypes", "unchecked"}) + @SuppressWarnings("unchecked") public void test() throws NoSuchFieldException, IllegalAccessException { // given EzyBeanContextBuilder builder = new EzySimpleBeanContext.Builder(); @@ -37,7 +36,6 @@ public void test() throws NoSuchFieldException, IllegalAccessException { GraphQLConfiguration sut = new GraphQLConfiguration(); EzySingletonFactory singletonFactory = context.getSingletonFactory(); sut.setSingletonFactory(singletonFactory); - sut.setObjectMapper(new ObjectMapper()); sut.setGraphQLEnable(true); sut.setGraphQLAuthenticated(true); @@ -75,7 +73,6 @@ public void enableFalseTest() throws NoSuchFieldException, IllegalAccessExceptio GraphQLConfiguration sut = new GraphQLConfiguration(); EzySingletonFactory singletonFactory = context.getSingletonFactory(); sut.setSingletonFactory(singletonFactory); - sut.setObjectMapper(new ObjectMapper()); sut.setGraphQLEnable(false); // when diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/controller/GraphQLControllerTest.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/controller/GraphQLControllerTest.java index 691e9045..822b50b3 100644 --- a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/controller/GraphQLControllerTest.java +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/controller/GraphQLControllerTest.java @@ -6,28 +6,34 @@ import com.tvd12.ezyhttp.core.exception.HttpNotFoundException; import com.tvd12.ezyhttp.server.core.request.RequestArguments; import com.tvd12.ezyhttp.server.graphql.controller.GraphQLController; +import com.tvd12.ezyhttp.server.graphql.data.GraphQLDataFilter; import com.tvd12.ezyhttp.server.graphql.data.GraphQLRequest; import com.tvd12.ezyhttp.server.graphql.exception.GraphQLInvalidSchemeException; +import com.tvd12.ezyhttp.server.graphql.exception.GraphQLObjectMapperException; import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLDataFetcher; import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLDataFetcherManager; import com.tvd12.ezyhttp.server.graphql.interceptor.GraphQLInterceptor; import com.tvd12.ezyhttp.server.graphql.interceptor.GraphQLInterceptorManager; +import com.tvd12.ezyhttp.server.graphql.json.GraphQLObjectMapperFactory; +import com.tvd12.ezyhttp.server.graphql.query.GraphQLQueryDefinition; import com.tvd12.ezyhttp.server.graphql.scheme.GraphQLSchemaParser; import com.tvd12.ezyhttp.server.graphql.test.datafetcher.*; import com.tvd12.test.assertion.Asserts; import org.testng.annotations.Test; import java.util.Collections; +import java.util.Map; import static org.mockito.Mockito.*; -@SuppressWarnings("rawtypes") public class GraphQLControllerTest { @Test public void test() throws Exception { // given - GraphQLSchemaParser schemaParser = new GraphQLSchemaParser(); + GraphQLSchemaParser schemaParser = new GraphQLSchemaParser( + new ObjectMapper() + ); GraphQLDataFetcher meDataFetcher = new GraphQLMeDataFetcher(); GraphQLDataFetcher heroDataFetcher = new GraphQLHeroDataFetcher(); GraphQLDataFetcherManager dataFetcherManager = GraphQLDataFetcherManager.builder() @@ -43,7 +49,7 @@ public void test() throws Exception { any(RequestArguments.class), any(String.class), any(String.class), - any(Object.class), + any(GraphQLQueryDefinition.class), any(GraphQLDataFetcher.class) ) ).thenReturn(true); @@ -55,6 +61,7 @@ public void test() throws Exception { GraphQLController controller = GraphQLController.builder() .schemaParser(schemaParser) + .dataFilter(new GraphQLDataFilter()) .dataFetcherManager(dataFetcherManager) .objectMapper(objectMapper) .interceptorManager(interceptorManager) @@ -71,7 +78,7 @@ public void test() throws Exception { // then Asserts.assertFalse(controller.isAuthenticated()); - Asserts.assertEquals(meResult.toString(), "{me={bank={id=1}, name=Dzung, friends=[{name=Foo}, {name=Bar}]}}"); + Asserts.assertEquals(meResult.toString(), "{me={bank={id=100}, name=Dzung, friends=[{name=Foo}, {name=Bar}]}}"); Asserts.assertEquals(heroResult.toString(), "{hero=Hero 007}"); verify(interceptorManager, times(2)).getRequestInterceptors(); @@ -81,13 +88,14 @@ public void test() throws Exception { any(RequestArguments.class), any(String.class), any(String.class), - any(Object.class), + any(GraphQLQueryDefinition.class), any(GraphQLDataFetcher.class) ); verify(interceptor, times(2)).postHandle( any(RequestArguments.class), any(String.class), any(String.class), + any(GraphQLQueryDefinition.class), any(Object.class), any(GraphQLDataFetcher.class) ); @@ -96,9 +104,11 @@ public void test() throws Exception { } @Test - public void getAllFieldsTest() throws Exception { + public void getAllFieldsTest() { // given - GraphQLSchemaParser schemaParser = new GraphQLSchemaParser(); + GraphQLSchemaParser schemaParser = new GraphQLSchemaParser( + new ObjectMapper() + ); GraphQLDataFetcher meDataFetcher = new GraphQLMeDataFetcher(); GraphQLDataFetcher heroDataFetcher = new GraphQLHeroDataFetcher(); GraphQLDataFetcherManager dataFetcherManager = GraphQLDataFetcherManager.builder() @@ -114,7 +124,7 @@ public void getAllFieldsTest() throws Exception { any(RequestArguments.class), any(String.class), any(String.class), - any(Object.class), + any(GraphQLQueryDefinition.class), any(GraphQLDataFetcher.class) ) ).thenReturn(true); @@ -126,6 +136,7 @@ public void getAllFieldsTest() throws Exception { GraphQLController controller = GraphQLController.builder() .schemaParser(schemaParser) + .dataFilter(new GraphQLDataFilter()) .dataFetcherManager(dataFetcherManager) .objectMapper(objectMapper) .interceptorManager(interceptorManager) @@ -139,7 +150,7 @@ public void getAllFieldsTest() throws Exception { // then Asserts.assertFalse(controller.isAuthenticated()); - Asserts.assertEquals(meResult.toString(), "{me={bank={id=100}, address=null, nickName=Hello, name=Dzung, id=1, friends=[{id=1, name=Foo}, {id=1, name=Bar}]}}"); + Asserts.assertEquals(meResult.toString(), "{me={bank={id=100}, nickName=Hello, name=Dzung, id=1, friends=[{id=1, name=Foo}, {id=1, name=Bar}]}}"); verify(interceptorManager, times(1)).getRequestInterceptors(); verifyNoMoreInteractions(interceptorManager); @@ -148,13 +159,14 @@ public void getAllFieldsTest() throws Exception { any(RequestArguments.class), any(String.class), any(String.class), - any(Object.class), + any(GraphQLQueryDefinition.class), any(GraphQLDataFetcher.class) ); verify(interceptor, times(1)).postHandle( any(RequestArguments.class), any(String.class), any(String.class), + any(GraphQLQueryDefinition.class), any(Object.class), any(GraphQLDataFetcher.class) ); @@ -163,9 +175,11 @@ public void getAllFieldsTest() throws Exception { } @Test - public void getAllFriendFields() throws Exception { + public void getAllFriendFields() { // given - GraphQLSchemaParser schemaParser = new GraphQLSchemaParser(); + GraphQLSchemaParser schemaParser = new GraphQLSchemaParser( + new ObjectMapper() + ); GraphQLDataFetcher meDataFetcher = new GraphQLMeDataFetcher(); GraphQLDataFetcher heroDataFetcher = new GraphQLHeroDataFetcher(); GraphQLDataFetcherManager dataFetcherManager = GraphQLDataFetcherManager.builder() @@ -181,7 +195,7 @@ public void getAllFriendFields() throws Exception { any(RequestArguments.class), any(String.class), any(String.class), - any(Object.class), + any(GraphQLQueryDefinition.class), any(GraphQLDataFetcher.class) ) ).thenReturn(true); @@ -193,6 +207,7 @@ public void getAllFriendFields() throws Exception { GraphQLController controller = GraphQLController.builder() .schemaParser(schemaParser) + .dataFilter(new GraphQLDataFilter()) .dataFetcherManager(dataFetcherManager) .objectMapper(objectMapper) .interceptorManager(interceptorManager) @@ -209,7 +224,7 @@ public void getAllFriendFields() throws Exception { // then Asserts.assertFalse(controller.isAuthenticated()); - Asserts.assertEquals(meResult.toString(), "{me={bank={id=1}, name=Dzung, friends=[{name=Foo, id=1}, {name=Bar, id=1}]}}"); + Asserts.assertEquals(meResult.toString(), "{me={bank={id=100}, name=Dzung, friends=[{name=Foo, id=1}, {name=Bar, id=1}]}}"); Asserts.assertEquals(heroResult.toString(), "{hero=Hero 007}"); verify(interceptorManager, times(2)).getRequestInterceptors(); @@ -219,13 +234,14 @@ public void getAllFriendFields() throws Exception { any(RequestArguments.class), any(String.class), any(String.class), - any(Object.class), + any(GraphQLQueryDefinition.class), any(GraphQLDataFetcher.class) ); verify(interceptor, times(2)).postHandle( any(RequestArguments.class), any(String.class), any(String.class), + any(GraphQLQueryDefinition.class), any(Object.class), any(GraphQLDataFetcher.class) ); @@ -237,7 +253,9 @@ public void getAllFriendFields() throws Exception { @Test public void testFetcherNotFoundException() { // given - GraphQLSchemaParser schemaParser = new GraphQLSchemaParser(); + GraphQLSchemaParser schemaParser = new GraphQLSchemaParser( + new ObjectMapper() + ); GraphQLDataFetcherManager dataFetcherManager = GraphQLDataFetcherManager.builder().build(); ObjectMapper objectMapper = new ObjectMapper(); @@ -261,7 +279,9 @@ public void testFetcherNotFoundException() { @Test public void testInterceptorFalse() { // given - GraphQLSchemaParser schemaParser = new GraphQLSchemaParser(); + GraphQLSchemaParser schemaParser = new GraphQLSchemaParser( + new ObjectMapper() + ); GraphQLDataFetcher heroDataFetcher = new GraphQLHeroDataFetcher(); GraphQLDataFetcherManager dataFetcherManager = GraphQLDataFetcherManager.builder() .addDataFetcher(heroDataFetcher) @@ -275,7 +295,7 @@ public void testInterceptorFalse() { any(RequestArguments.class), any(String.class), any(String.class), - any(Object.class), + any(GraphQLQueryDefinition.class), any(GraphQLDataFetcher.class) ) ).thenReturn(false); @@ -307,7 +327,7 @@ public void testInterceptorFalse() { any(RequestArguments.class), any(String.class), any(String.class), - any(Object.class), + any(GraphQLQueryDefinition.class), any(GraphQLDataFetcher.class) ); verifyNoMoreInteractions(interceptor); @@ -315,9 +335,11 @@ public void testInterceptorFalse() { } @Test - public void testQueryWithVariables() throws Exception { + public void testQueryWithVariables() { // given - GraphQLSchemaParser schemaParser = new GraphQLSchemaParser(); + GraphQLSchemaParser schemaParser = new GraphQLSchemaParser( + new GraphQLObjectMapperFactory().newObjectMapper() + ); GraphQLDataFetcher welcomeDataFetcher = new GraphQLWelcomeDataFetcher(); GraphQLDataFetcherManager dataFetcherManager = GraphQLDataFetcherManager.builder() @@ -332,7 +354,7 @@ public void testQueryWithVariables() throws Exception { any(RequestArguments.class), any(String.class), any(String.class), - any(Object.class), + any(GraphQLQueryDefinition.class), any(GraphQLDataFetcher.class) ) ).thenReturn(true); @@ -349,14 +371,17 @@ public void testQueryWithVariables() throws Exception { .interceptorManager(interceptorManager) .build(); - String welcomeQuery = "{welcome}"; - String variables = "{\"name\": \"Foo\"}"; + String welcomeQuery = "{welcome(name: $name)}"; + Map variables = Collections.singletonMap( + "name", "Foo" + ); + String variablesString = "{\"name\":\"Foo\"}"; GraphQLRequest welcomeRequest = new GraphQLRequest(); welcomeRequest.setQuery(welcomeQuery); welcomeRequest.setVariables(variables); // when - Object welcomeResult1 = controller.doGet(arguments, welcomeQuery, variables); + Object welcomeResult1 = controller.doGet(arguments, welcomeQuery, variablesString); Object welcomeResult2 = controller.doPost(arguments, welcomeRequest); // then @@ -370,13 +395,14 @@ public void testQueryWithVariables() throws Exception { any(RequestArguments.class), any(String.class), any(String.class), - any(Object.class), + any(GraphQLQueryDefinition.class), any(GraphQLDataFetcher.class) ); verify(interceptor, times(2)).postHandle( any(RequestArguments.class), any(String.class), any(String.class), + any(GraphQLQueryDefinition.class), any(Object.class), any(GraphQLDataFetcher.class) ); @@ -385,9 +411,11 @@ public void testQueryWithVariables() throws Exception { } @Test - public void testQueryWithNullVariableType() throws Exception { + public void testQueryWithNullVariableType() { // given - GraphQLSchemaParser schemaParser = new GraphQLSchemaParser(); + GraphQLSchemaParser schemaParser = new GraphQLSchemaParser( + new GraphQLObjectMapperFactory().newObjectMapper() + ); GraphQLDataFetcher fooDataFetcher = new GraphQLFooDataFetcher(); GraphQLDataFetcherManager dataFetcherManager = GraphQLDataFetcherManager.builder() .addDataFetcher(fooDataFetcher) @@ -401,7 +429,7 @@ public void testQueryWithNullVariableType() throws Exception { any(RequestArguments.class), any(String.class), any(String.class), - any(Object.class), + any(GraphQLQueryDefinition.class), any(GraphQLDataFetcher.class) ) ).thenReturn(true); @@ -413,20 +441,21 @@ public void testQueryWithNullVariableType() throws Exception { GraphQLController controller = GraphQLController.builder() .schemaParser(schemaParser) + .dataFilter(new GraphQLDataFilter()) .dataFetcherManager(dataFetcherManager) .objectMapper(objectMapper) .interceptorManager(interceptorManager) .build(); - String fooQuery = "{foo}"; + String fooQuery = "{foo{value(value:$value){*}}}"; // when Object fooResult1 = controller.doGet(arguments, fooQuery, "{\"value\": \"Bar\"}"); Object fooResult2 = controller.doGet(arguments, fooQuery, null); // then - Asserts.assertEquals(fooResult1.toString(), "{foo=Foo {value=Bar}}"); - Asserts.assertEquals(fooResult2.toString(), "{foo=Foo null}"); + Asserts.assertEquals(fooResult1.toString(), "{foo={value={bar=Bar}}}"); + Asserts.assertEquals(fooResult2.toString(), "{foo={value={}}}"); verify(interceptorManager, times(2)).getRequestInterceptors(); verifyNoMoreInteractions(interceptorManager); @@ -435,13 +464,14 @@ public void testQueryWithNullVariableType() throws Exception { any(RequestArguments.class), any(String.class), any(String.class), - any(Object.class), + any(GraphQLQueryDefinition.class), any(GraphQLDataFetcher.class) ); verify(interceptor, times(2)).postHandle( any(RequestArguments.class), any(String.class), any(String.class), + any(GraphQLQueryDefinition.class), any(Object.class), any(GraphQLDataFetcher.class) ); @@ -452,7 +482,9 @@ public void testQueryWithNullVariableType() throws Exception { @Test public void testInvalidScheme() { // given - GraphQLSchemaParser schemaParser = new GraphQLSchemaParser(); + GraphQLSchemaParser schemaParser = new GraphQLSchemaParser( + new ObjectMapper() + ); GraphQLDataFetcher meDataFetcher = new GraphQLYouDataFetcher(); GraphQLDataFetcherManager dataFetcherManager = GraphQLDataFetcherManager.builder() .addDataFetcher(meDataFetcher) @@ -466,7 +498,7 @@ public void testInvalidScheme() { any(RequestArguments.class), any(String.class), any(String.class), - any(Object.class), + any(GraphQLQueryDefinition.class), any(GraphQLDataFetcher.class) ) ).thenReturn(true); @@ -478,6 +510,7 @@ public void testInvalidScheme() { GraphQLController controller = GraphQLController.builder() .schemaParser(schemaParser) + .dataFilter(new GraphQLDataFilter()) .dataFetcherManager(dataFetcherManager) .objectMapper(objectMapper) .interceptorManager(interceptorManager) @@ -499,7 +532,7 @@ public void testInvalidScheme() { any(RequestArguments.class), any(String.class), any(String.class), - any(Object.class), + any(GraphQLQueryDefinition.class), any(GraphQLDataFetcher.class) ); verifyNoMoreInteractions(interceptor); @@ -517,4 +550,30 @@ public void testNoNameDataFetcher() { // then Asserts.assertEquals(EzyNotImplementedException.class.toString(), e.getClass().toString()); } + + @Test + public void doGetTestException() { + // given + GraphQLSchemaParser schemaParser = new GraphQLSchemaParser( + new ObjectMapper() + ); + GraphQLDataFetcherManager dataFetcherManager = GraphQLDataFetcherManager.builder().build(); + ObjectMapper objectMapper = new ObjectMapper(); + + RequestArguments arguments = mock(RequestArguments.class); + + GraphQLController controller = GraphQLController.builder() + .schemaParser(schemaParser) + .dataFetcherManager(dataFetcherManager) + .objectMapper(objectMapper) + .build(); + + String heroQuery = "{hero}"; + + // when + Throwable e = Asserts.assertThrows(() -> controller.doGet(arguments, heroQuery, "abc")); + + // then + Asserts.assertEqualsType(e, GraphQLObjectMapperException.class); + } } diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/data/GraphQLFieldTest.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/data/GraphQLFieldTest.java index 600d8abc..274545cf 100644 --- a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/data/GraphQLFieldTest.java +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/data/GraphQLFieldTest.java @@ -1,10 +1,13 @@ package com.tvd12.ezyhttp.server.graphql.test.data; +import com.tvd12.ezyfox.util.EzyMapBuilder; import com.tvd12.ezyhttp.server.graphql.data.GraphQLField; import com.tvd12.test.assertion.Asserts; import com.tvd12.test.util.RandomUtil; import org.testng.annotations.Test; +import java.util.Collections; + public class GraphQLFieldTest { @Test @@ -30,5 +33,54 @@ public void test() { Asserts.assertEquals(fieldA.toString(), fieldName + ", []"); Asserts.assertEquals(fieldB.toString(), fieldName + ", [" + fieldA + "]"); Asserts.assertEquals(fieldA.hashCode(), fieldName.hashCode()); + Asserts.assertEquals( + fieldB.getFieldByName(), + Collections.singletonMap(fieldName, fieldA), + false + ); + Asserts.assertNull(fieldA.getArgumentValue("alo")); + Asserts.assertNull(fieldA.getArgumentValue("alo", String.class)); + Asserts.assertNull(fieldA.getFieldArgumentValue("alo", fieldName)); + Asserts.assertNull(fieldA.getFieldArgumentValue("alo", String.class, fieldName)); + } + + @Test + public void getArgumentValueByNameAndTypeTest() { + // given + String fieldName = RandomUtil.randomShortAlphabetString(); + GraphQLField.Builder builder = GraphQLField.builder() + .name(fieldName) + .arguments( + EzyMapBuilder.mapBuilder() + .put("hello", 1) + .put("world", "2") + .toMap() + ); + GraphQLField fieldA = builder.build(); + + // when + // then + Asserts.assertEquals( + fieldA.getArgumentValue("hello", long.class), + 1L + ); + Asserts.assertEquals( + fieldA.getArgumentValue("world", long.class), + 2L + ); + System.out.println(builder); + System.out.println(fieldA); + } + + @Test + public void toStringTest() { + // given + // when + // then + System.out.println( + GraphQLField.builder() + .name("hello") + .arguments(Collections.emptyMap()) + ); } } diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLAbstractDataFetcherTest.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLAbstractDataFetcherTest.java index 68ce0b48..e5f5e127 100644 --- a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLAbstractDataFetcherTest.java +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLAbstractDataFetcherTest.java @@ -1,10 +1,9 @@ package com.tvd12.ezyhttp.server.graphql.test.datafetcher; -import com.tvd12.ezyhttp.server.graphql.test.datafetcher.GraphQLNoNameDataFetcher; +import com.tvd12.ezyfox.exception.EzyNotImplementedException; import com.tvd12.test.assertion.Asserts; import org.testng.annotations.Test; -@SuppressWarnings("rawtypes") public class GraphQLAbstractDataFetcherTest { @Test @@ -13,9 +12,11 @@ public void testExceptionWhenGetArgumentType() { GraphQLNoNameDataFetcher noNameDataFetcher = new GraphQLNoNameDataFetcher(); // when - Class c = noNameDataFetcher.getParameterType(); + Throwable e = Asserts.assertThrows(() -> + System.out.println(noNameDataFetcher.getQueryName()) + ); // then - Asserts.assertNull(c); + Asserts.assertEqualsType(e, EzyNotImplementedException.class); } } diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLFooDataFetcher.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLFooDataFetcher.java index 4e1d66b1..00407655 100644 --- a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLFooDataFetcher.java +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLFooDataFetcher.java @@ -1,12 +1,13 @@ package com.tvd12.ezyhttp.server.graphql.test.datafetcher; import com.tvd12.ezyfox.bean.annotation.EzySingleton; +import com.tvd12.ezyfox.util.EzyMapBuilder; import com.tvd12.ezyhttp.server.core.request.RequestArguments; -import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLDataFetcher; import com.tvd12.ezyhttp.server.graphql.annotation.GraphQLQuery; +import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLDataFetcher; +import com.tvd12.ezyhttp.server.graphql.query.GraphQLQueryDefinition; -@SuppressWarnings("rawtypes") @EzySingleton @GraphQLQuery(name = "foo") public class GraphQLFooDataFetcher implements GraphQLDataFetcher { @@ -14,8 +15,20 @@ public class GraphQLFooDataFetcher implements GraphQLDataFetcher { @Override public Object getData( RequestArguments arguments, - Object argument + GraphQLQueryDefinition query ) { - return "Foo " + argument; + String value = query.getFieldArgumentValue( + "value", + String.class, + "value" + ); + return EzyMapBuilder.mapBuilder() + .put( + "value", + EzyMapBuilder.mapBuilder() + .put("bar", value) + .toMap() + ) + .toMap(); } } diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLHeroDataFetcher.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLHeroDataFetcher.java index d6d9ab7f..8ead842f 100644 --- a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLHeroDataFetcher.java +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLHeroDataFetcher.java @@ -3,15 +3,16 @@ import com.tvd12.ezyfox.bean.annotation.EzySingleton; import com.tvd12.ezyhttp.server.core.request.RequestArguments; import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLAbstractDataFetcher; +import com.tvd12.ezyhttp.server.graphql.query.GraphQLQueryDefinition; @EzySingleton public class GraphQLHeroDataFetcher - extends GraphQLAbstractDataFetcher { + extends GraphQLAbstractDataFetcher { public String getData( RequestArguments arguments, - Object parameter + GraphQLQueryDefinition query ) { return "Hero 007"; } diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLMeDataFetcher.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLMeDataFetcher.java index bad83739..f8a0dd94 100644 --- a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLMeDataFetcher.java +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLMeDataFetcher.java @@ -3,6 +3,7 @@ import com.tvd12.ezyfox.bean.annotation.EzySingleton; import com.tvd12.ezyhttp.server.core.request.RequestArguments; import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLAbstractDataFetcher; +import com.tvd12.ezyhttp.server.graphql.query.GraphQLQueryDefinition; import lombok.Builder; import lombok.Data; import lombok.Getter; @@ -12,11 +13,11 @@ @EzySingleton public class GraphQLMeDataFetcher - extends GraphQLAbstractDataFetcher { + extends GraphQLAbstractDataFetcher { public MeResponse getData( RequestArguments arguments, - MeRequest parameter + GraphQLQueryDefinition query ) { return MeResponse.builder() .id(1) diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLNoNameDataFetcher.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLNoNameDataFetcher.java index b3bd5db6..bef69f5e 100644 --- a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLNoNameDataFetcher.java +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLNoNameDataFetcher.java @@ -3,17 +3,17 @@ import com.tvd12.ezyfox.bean.annotation.EzySingleton; import com.tvd12.ezyhttp.server.core.request.RequestArguments; import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLAbstractDataFetcher; +import com.tvd12.ezyhttp.server.graphql.query.GraphQLQueryDefinition; @EzySingleton -@SuppressWarnings("rawtypes") public class GraphQLNoNameDataFetcher extends GraphQLAbstractDataFetcher { @Override public Object getData( RequestArguments arguments, - Object parameter + GraphQLQueryDefinition query ) { - return "Foo " + parameter; + return "Foo " + query; } } diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLWelcomeDataFetcher.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLWelcomeDataFetcher.java index 375a299c..35327fef 100644 --- a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLWelcomeDataFetcher.java +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLWelcomeDataFetcher.java @@ -3,18 +3,18 @@ import com.tvd12.ezyfox.bean.annotation.EzySingleton; import com.tvd12.ezyhttp.server.core.request.RequestArguments; import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLDataFetcher; -import com.tvd12.ezyhttp.server.graphql.test.datafetcher.GraphQLWelcomeDataFetcher.WelcomeRequest; +import com.tvd12.ezyhttp.server.graphql.query.GraphQLQueryDefinition; import lombok.Data; @EzySingleton -public class GraphQLWelcomeDataFetcher implements GraphQLDataFetcher { +public class GraphQLWelcomeDataFetcher implements GraphQLDataFetcher { public String getData( RequestArguments arguments, - WelcomeRequest parameter + GraphQLQueryDefinition query ) { - return "Welcome " + parameter.getName(); + return "Welcome " + query.getArgumentValue("name"); } @Override diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLYouDataFetcher.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLYouDataFetcher.java index 0a0f001d..98642864 100644 --- a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLYouDataFetcher.java +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/datafetcher/GraphQLYouDataFetcher.java @@ -2,19 +2,20 @@ import com.tvd12.ezyfox.bean.annotation.EzySingleton; import com.tvd12.ezyhttp.server.core.request.RequestArguments; -import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLAbstractDataFetcher; import com.tvd12.ezyhttp.server.graphql.annotation.GraphQLQuery; +import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLAbstractDataFetcher; +import com.tvd12.ezyhttp.server.graphql.query.GraphQLQueryDefinition; import lombok.Builder; import lombok.Getter; @EzySingleton @GraphQLQuery(name = "you") public class GraphQLYouDataFetcher - extends GraphQLAbstractDataFetcher { + extends GraphQLAbstractDataFetcher { public YouResponse getData( RequestArguments arguments, - Object argument + GraphQLQueryDefinition query ) { return YouResponse.builder() .friends("A, B") diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/exception/GraphQLInvalidSchemeExceptionTest.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/exception/GraphQLInvalidSchemeExceptionTest.java new file mode 100644 index 00000000..932e14c6 --- /dev/null +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/exception/GraphQLInvalidSchemeExceptionTest.java @@ -0,0 +1,23 @@ +package com.tvd12.ezyhttp.server.graphql.test.exception; + +import com.tvd12.ezyhttp.server.graphql.exception.GraphQLInvalidSchemeException; +import com.tvd12.test.assertion.Asserts; +import com.tvd12.test.util.RandomUtil; +import org.testng.annotations.Test; + +public class GraphQLInvalidSchemeExceptionTest { + + @Test + public void test() { + // given + String errors = RandomUtil.randomShortAlphabetString(); + + // + GraphQLInvalidSchemeException instance = new GraphQLInvalidSchemeException( + errors + ); + + // then + Asserts.assertEquals(instance.getErrors(), errors); + } +} diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/exception/GraphQLObjectMapperExceptionTest.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/exception/GraphQLObjectMapperExceptionTest.java new file mode 100644 index 00000000..66805816 --- /dev/null +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/exception/GraphQLObjectMapperExceptionTest.java @@ -0,0 +1,26 @@ +package com.tvd12.ezyhttp.server.graphql.test.exception; + +import com.tvd12.ezyhttp.server.graphql.exception.GraphQLObjectMapperException; +import com.tvd12.test.assertion.Asserts; +import com.tvd12.test.util.RandomUtil; +import org.testng.annotations.Test; + +public class GraphQLObjectMapperExceptionTest { + + @Test + public void test() { + // given + String errors = RandomUtil.randomShortAlphabetString(); + Exception cause = new Exception("test"); + + // + GraphQLObjectMapperException instance = new GraphQLObjectMapperException( + errors, + cause + ); + + // then + Asserts.assertEquals(instance.getErrors(), errors); + Asserts.assertEquals(instance.getCause(), cause); + } +} diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/fetcher/GraphQLDataFetcherManagerTest.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/fetcher/GraphQLDataFetcherManagerTest.java index ac7737bb..2ac6ec31 100644 --- a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/fetcher/GraphQLDataFetcherManagerTest.java +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/fetcher/GraphQLDataFetcherManagerTest.java @@ -4,6 +4,7 @@ import com.tvd12.ezyhttp.server.core.request.RequestArguments; import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLDataFetcher; import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLDataFetcherManager; +import com.tvd12.ezyhttp.server.graphql.query.GraphQLQueryDefinition; import com.tvd12.test.assertion.Asserts; import org.testng.annotations.Test; import org.testng.collections.Sets; @@ -52,12 +53,12 @@ public void test() { ); } - private static class Fetcher1 implements GraphQLDataFetcher { + private static class Fetcher1 implements GraphQLDataFetcher { @Override public String getData( RequestArguments arguments, - String parameter + GraphQLQueryDefinition query ) { return null; } @@ -68,12 +69,12 @@ public String getQueryName() { } } - private static class Fetcher11 implements GraphQLDataFetcher { + private static class Fetcher11 implements GraphQLDataFetcher { @Override public String getData( RequestArguments arguments, - String parameter + GraphQLQueryDefinition query ) { return null; } @@ -84,12 +85,12 @@ public String getQueryName() { } } - private static class Fetcher2 implements GraphQLDataFetcher { + private static class Fetcher2 implements GraphQLDataFetcher { @Override public String getData( RequestArguments arguments, - String parameter + GraphQLQueryDefinition query ) { return null; } @@ -100,12 +101,12 @@ public String getQueryName() { } } - private static class Fetcher22 implements GraphQLDataFetcher { + private static class Fetcher22 implements GraphQLDataFetcher { @Override public String getData( RequestArguments arguments, - String parameter + GraphQLQueryDefinition query ) { return null; } diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/fetcher/GraphQLDataFetcherTest.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/fetcher/GraphQLDataFetcherTest.java index d2e3fbb8..6cd7271d 100644 --- a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/fetcher/GraphQLDataFetcherTest.java +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/fetcher/GraphQLDataFetcherTest.java @@ -2,6 +2,7 @@ import com.tvd12.ezyhttp.server.core.request.RequestArguments; import com.tvd12.ezyhttp.server.graphql.fetcher.GraphQLDataFetcher; +import com.tvd12.ezyhttp.server.graphql.query.GraphQLQueryDefinition; import com.tvd12.test.assertion.Asserts; import org.testng.annotations.Test; @@ -14,15 +15,18 @@ public void test() { // when // then - Asserts.assertNull(instance.getRequestScheme()); + Asserts.assertNull(instance.getQueryScheme()); Asserts.assertNull(instance.getResponseScheme()); } private static class ExGraphQLDataFetcher - implements GraphQLDataFetcher { + implements GraphQLDataFetcher { @Override - public String getData(RequestArguments arguments, String parameter) { + public String getData( + RequestArguments arguments, + GraphQLQueryDefinition query + ) { return null; } } diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/interceptor/GraphQLInterceptorTest.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/interceptor/GraphQLInterceptorTest.java index e75ff789..70abb70f 100644 --- a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/interceptor/GraphQLInterceptorTest.java +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/interceptor/GraphQLInterceptorTest.java @@ -24,6 +24,7 @@ public void test() { null, null, null, + null, null ); int priority = interceptor.getPriority(); diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/scheme/GraphQLDataSchemaTest.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/scheme/GraphQLDataSchemaTest.java index cab6dd6b..b5b195d7 100644 --- a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/scheme/GraphQLDataSchemaTest.java +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/scheme/GraphQLDataSchemaTest.java @@ -20,13 +20,14 @@ public void test() { String type = GraphQLDataTypes.BOOLEAN; String format = RandomUtil.randomShortAlphabetString(); String description = RandomUtil.randomShortAlphabetString(); + List arguments = Collections.emptyList(); GraphQLDataSchema items = GraphQLDataSchema.builder() - .name(RandomUtil.randomShortAlphabetString()) - .type(RandomUtil.randomShortAlphabetString()) - .format(GraphQLDataFormats.DOUBLE) - .description(RandomUtil.randomShortAlphabetString()) - .build(); + .name(RandomUtil.randomShortAlphabetString()) + .type(RandomUtil.randomShortAlphabetString()) + .format(GraphQLDataFormats.DOUBLE) + .description(RandomUtil.randomShortAlphabetString()) + .build(); List properties = Collections.singletonList( GraphQLDataSchema.builder() @@ -53,6 +54,7 @@ public void test() { .items(items) .properties(properties) .examples(examples) + .arguments(arguments) .build(); // then @@ -63,6 +65,7 @@ public void test() { Asserts.assertEquals(instance.getExamples(), examples); Asserts.assertEquals(instance.getItems(), items); Asserts.assertEquals(instance.getProperties(), properties); + Asserts.assertEquals(instance.getArguments(), arguments); System.out.println(instance); } diff --git a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/scheme/GraphQLSchemaParserTest.java b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/scheme/GraphQLSchemaParserTest.java index fe558f5a..1d54cf01 100644 --- a/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/scheme/GraphQLSchemaParserTest.java +++ b/ezyhttp-server-graphql/src/test/java/com/tvd12/ezyhttp/server/graphql/test/scheme/GraphQLSchemaParserTest.java @@ -1,37 +1,50 @@ package com.tvd12.ezyhttp.server.graphql.test.scheme; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.tvd12.ezyfox.util.EzyMapBuilder; +import com.tvd12.ezyhttp.server.graphql.exception.GraphQLObjectMapperException; +import com.tvd12.ezyhttp.server.graphql.json.GraphQLObjectMapperFactory; import com.tvd12.ezyhttp.server.graphql.scheme.GraphQLSchema; import com.tvd12.ezyhttp.server.graphql.scheme.GraphQLSchemaParser; import com.tvd12.test.assertion.Asserts; +import com.tvd12.test.reflect.MethodInvoker; import com.tvd12.test.util.RandomUtil; import org.testng.annotations.Test; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; + public class GraphQLSchemaParserTest { @Test public void testStandardize1() { // given - GraphQLSchemaParser parser = new GraphQLSchemaParser(); + GraphQLSchemaParser parser = new GraphQLSchemaParser( + new ObjectMapper() + ); String query = "{}"; // when - GraphQLSchema schema = parser.parseQuery(query); - Throwable e = Asserts.assertThrows(() -> schema.getQueryDefinitions().get(0).getName()); + GraphQLSchema schema = parser.parseQuery(query, Collections.emptyMap()); + System.out.println(schema.getQueryDefinitions().get(0).getName()); // then Asserts.assertEquals(schema.getQueryDefinitions().size(), 1); - Asserts.assertEquals(IllegalArgumentException.class, e.getClass()); } @Test public void testStandardize2() { // given - GraphQLSchemaParser parser = new GraphQLSchemaParser(); + GraphQLSchemaParser parser = new GraphQLSchemaParser( + new ObjectMapper() + ); String expectedQueryName = RandomUtil.randomShortAlphabetString(); String query = "{" + expectedQueryName + "}"; // when - GraphQLSchema schema = parser.parseQuery(query); + GraphQLSchema schema = parser.parseQuery(query, Collections.emptyMap()); // then Asserts.assertEquals(schema.getQueryDefinitions().size(), 1); @@ -41,7 +54,9 @@ public void testStandardize2() { @Test public void testStandardize3() { // given - GraphQLSchemaParser parser = new GraphQLSchemaParser(); + GraphQLSchemaParser parser = new GraphQLSchemaParser( + new ObjectMapper() + ); String[] queries = { "{queryName}", @@ -62,7 +77,7 @@ public void testStandardize3() { // when for (int i = 0; i < numQueries; ++i) { - schemas[i] = parser.parseQuery(queries[i]); + schemas[i] = parser.parseQuery(queries[i], Collections.emptyMap()); } // then for (int i = 0; i < numQueries; ++i) { @@ -74,7 +89,9 @@ public void testStandardize3() { @Test public void testStandardize4() { // given - GraphQLSchemaParser parser = new GraphQLSchemaParser(); + GraphQLSchemaParser parser = new GraphQLSchemaParser( + new ObjectMapper() + ); String[] queries = { "{queryName{field1 field2}}", @@ -90,7 +107,7 @@ public void testStandardize4() { // when for (int i = 0; i < numQueries; ++i) { - schemas[i] = parser.parseQuery(queries[i]); + schemas[i] = parser.parseQuery(queries[i], Collections.emptyMap()); } // then for (int i = 0; i < numQueries; ++i) { @@ -103,10 +120,12 @@ public void testStandardize4() { @Test public void testStandardize5() { // given - GraphQLSchemaParser parser = new GraphQLSchemaParser(); + GraphQLSchemaParser parser = new GraphQLSchemaParser( + new ObjectMapper() + ); // when - GraphQLSchema schema = parser.parseQuery(null); + GraphQLSchema schema = parser.parseQuery(null, Collections.emptyMap()); // then Asserts.assertEquals(schema.getQueryDefinitions().size(), 0); @@ -116,7 +135,9 @@ public void testStandardize5() { @Test public void testParseSchema1() { // given - GraphQLSchemaParser parser = new GraphQLSchemaParser(); + GraphQLSchemaParser parser = new GraphQLSchemaParser( + new ObjectMapper() + ); String[] queries = { "{q1{f1 f2{f21} f3{f31 f32}} q2}", @@ -131,7 +152,7 @@ public void testParseSchema1() { // when for (int i = 0; i < numQueries; ++i) { - schemas[i] = parser.parseQuery(queries[i]); + schemas[i] = parser.parseQuery(queries[i], Collections.emptyMap()); } // then @@ -151,12 +172,14 @@ public void testParseSchema1() { @Test public void testParseSchema2() { // given - GraphQLSchemaParser parser = new GraphQLSchemaParser(); + GraphQLSchemaParser parser = new GraphQLSchemaParser( + new ObjectMapper() + ); String query = "{q1 q2}"; // when - GraphQLSchema schema = parser.parseQuery(query); + GraphQLSchema schema = parser.parseQuery(query, Collections.emptyMap()); // then Asserts.assertEquals(schema.getQueryDefinitions().size(), 2); @@ -167,12 +190,14 @@ public void testParseSchema2() { @Test public void testZeroNameLength() { // given - GraphQLSchemaParser parser = new GraphQLSchemaParser(); + GraphQLSchemaParser parser = new GraphQLSchemaParser( + new ObjectMapper() + ); String query = "{q1 q2{{}}}"; // when - GraphQLSchema schema = parser.parseQuery(query); + GraphQLSchema schema = parser.parseQuery(query, Collections.emptyMap()); // then Asserts.assertEquals(schema.getQueryDefinitions().size(), 2); @@ -181,4 +206,299 @@ public void testZeroNameLength() { Asserts.assertEquals(schema.getQueryDefinitions().get(1).getFields().get(0).getName(), null); Asserts.assertEquals(schema.getQueryDefinitions().get(1).getFields().get(0).getFields().get(0).getName(), null); } + + @Test + public void testWithArguments() { + // given + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); + objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); + GraphQLSchemaParser parser = new GraphQLSchemaParser( + objectMapper + ); + + String query = "{q1(hello: 1) q2(world: 2, foo: {\"Hello\": \"World\"}, bar:[1, 2, 3]){value(animal: 1){}}}"; + + // when + GraphQLSchema schema = parser.parseQuery(query, Collections.emptyMap()); + + // then + Asserts.assertEquals(schema.getQueryDefinitions().size(), 2); + Asserts.assertEquals(schema.getQueryDefinitions().get(0).getName(), "q1"); + Asserts.assertEquals(schema.getQueryDefinitions().get(1).getName(), "q2"); + Asserts.assertEquals(schema.getQueryDefinitions().get(1).getFields().get(0).getName(), "value"); + Asserts.assertEquals(schema.getQueryDefinitions().get(1).getFields().get(0).getFields().get(0).getName(), null); + Asserts.assertEquals( + schema.getQueryDefinitions().get(0).getArguments(), + EzyMapBuilder.mapBuilder() + .put("hello", 1) + .toMap(), + false + ); + Asserts.assertEquals( + schema.getQueryDefinitions().get(1).getArguments(), + EzyMapBuilder.mapBuilder() + .put("world", 2) + .put( + "foo", + EzyMapBuilder.mapBuilder() + .put("Hello", "World") + .toMap() + ) + .put( + "bar", + Arrays.asList(1, 2, 3) + ) + .toMap(), + false + ); + Asserts.assertEquals( + schema.getQueryDefinitions().get(1).getFields().get(0).getArguments(), + EzyMapBuilder.mapBuilder() + .put("animal", 1) + .toMap(), + false + ); + } + + @Test + public void parseQueryNormal() { + // given + GraphQLSchemaParser instance = new GraphQLSchemaParser( + new GraphQLObjectMapperFactory().newObjectMapper() + ); + + // when + GraphQLSchema schema = instance.parseQuery( + "{me(id: 1){}}", + Collections.emptyMap() + ); + + // then + Asserts.assertEquals( + schema.getQueryDefinitions().get(0).getArguments(), + EzyMapBuilder.mapBuilder() + .put("id", 1) + .toMap(), + false + ); + } + + @Test + public void parseQueryNormalWithArgs() { + // given + GraphQLSchemaParser instance = new GraphQLSchemaParser( + new GraphQLObjectMapperFactory().newObjectMapper() + ); + + // when + GraphQLSchema schema = instance.parseQuery( + "{me(id: 1, name: {'value' : \"hello\"}, age: {value: $variable , level: $level }){}}", + EzyMapBuilder.mapBuilder() + .put("variable", 33) + .put("level", 2025) + .toMap() + ); + + // then + Asserts.assertEquals( + schema.getQueryDefinitions().get(0).getArguments(), + EzyMapBuilder.mapBuilder() + .put("id", 1) + .put( + "name", + EzyMapBuilder.mapBuilder() + .put("value", "hello") + .toMap() + ) + .put( + "age", + EzyMapBuilder.mapBuilder() + .put("value", 33) + .put("level", 2025) + .toMap() + ) + .toMap(), + false + ); + } + + @Test + public void parseQueryNormalWithArgsSpecialCase() { + // given + GraphQLSchemaParser instance = new GraphQLSchemaParser( + new GraphQLObjectMapperFactory().newObjectMapper() + ); + + // when + GraphQLSchema schema = instance.parseQuery( + "{me(id: 1, name: {'va\\\'lue' : \"he\\\"llo\"}, age: {value: $variable , level: $level }){}}", + EzyMapBuilder.mapBuilder() + .put("variable", 33) + .put("level", 2025) + .toMap() + ); + + // then + Asserts.assertEquals( + schema.getQueryDefinitions().get(0).getArguments(), + EzyMapBuilder.mapBuilder() + .put("id", 1) + .put( + "name", + EzyMapBuilder.mapBuilder() + .put("va\'lue", "he\"llo") + .toMap() + ) + .put( + "age", + EzyMapBuilder.mapBuilder() + .put("value", 33) + .put("level", 2025) + .toMap() + ) + .toMap(), + false + ); + } + + @Test + public void parseQueryInvalidArgumentTest() { + // given + GraphQLSchemaParser instance = new GraphQLSchemaParser( + new ObjectMapper() + ); + + // when + Throwable e = Asserts.assertThrows(() -> + instance.parseQuery( + "{me(hello: world){}}", + Collections.emptyMap() + ) + ); + + // then + Asserts.assertEqualsType(e, GraphQLObjectMapperException.class); + } + + @Test + public void parseQueryInvalidArgument2Test() { + // given + GraphQLSchemaParser instance = new GraphQLSchemaParser( + new ObjectMapper() + ); + + // when + Throwable e = Asserts.assertThrows(() -> + instance.parseQuery( + "{me(hello: world}", + Collections.emptyMap() + ) + ); + + // then + Asserts.assertEqualsType(e, GraphQLObjectMapperException.class); + } + + @Test + public void parseQueryInvalidArgument3Test() { + // given + GraphQLSchemaParser instance = new GraphQLSchemaParser( + new ObjectMapper() + ); + + // when + Throwable e = Asserts.assertThrows(() -> + instance.parseQuery( + "{me(hello: $world", + Collections.singletonMap("hello", "world") + ) + ); + + // then + Asserts.assertEqualsType(e, GraphQLObjectMapperException.class); + } + + @Test + public void parseQueryInvalidArgument3xTest() { + // given + GraphQLSchemaParser instance = new GraphQLSchemaParser( + new ObjectMapper() + ); + + StringBuilder argumentsBuilder = new StringBuilder(); + String query = "{me(hello: $"; + int queryLength = query.length(); + + // when + Integer i = MethodInvoker.create() + .object(instance) + .method("extractQueryArguments") + .param(StringBuilder.class, argumentsBuilder) + .param(String.class, query) + .param(int.class, 0) + .param(int.class, queryLength) + .param(Map.class, Collections.singletonMap("hello", "world")) + .invoke(Integer.class); + + // then + Asserts.assertEquals(i, queryLength + 1); + } + + @Test + public void parseQueryInvalidArgument4Test() { + // given + GraphQLSchemaParser instance = new GraphQLSchemaParser( + new ObjectMapper() + ); + + // when + Throwable e = Asserts.assertThrows(() -> + instance.parseQuery( + "{me(hello: '\"world)", + Collections.emptyMap() + ) + ); + + // then + Asserts.assertEqualsType(e, GraphQLObjectMapperException.class); + } + + @Test + public void parseQueryInvalidArgument5Test() { + // given + GraphQLSchemaParser instance = new GraphQLSchemaParser( + new ObjectMapper() + ); + + // when + Throwable e = Asserts.assertThrows(() -> + instance.parseQuery( + "{me(hello: 'world)", + Collections.emptyMap() + ) + ); + + // then + Asserts.assertEqualsType(e, GraphQLObjectMapperException.class); + } + + @Test + public void parseQueryInvalidArgument6Test() { + // given + GraphQLSchemaParser instance = new GraphQLSchemaParser( + new ObjectMapper() + ); + + // when + Throwable e = Asserts.assertThrows(() -> + instance.parseQuery( + "{me(hello: \"world)", + Collections.emptyMap() + ) + ); + + // then + Asserts.assertEqualsType(e, GraphQLObjectMapperException.class); + } } diff --git a/ezyhttp-server-graphql/src/test/resources/AllTests.tng.xml b/ezyhttp-server-graphql/src/test/resources/AllTests.tng.xml index 86b5323a..f8a80680 100644 --- a/ezyhttp-server-graphql/src/test/resources/AllTests.tng.xml +++ b/ezyhttp-server-graphql/src/test/resources/AllTests.tng.xml @@ -8,6 +8,7 @@ +