Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[master] Add Query Parameter Annotation Mapping for Http Service #2152

Merged
merged 5 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
// under the License.

import ballerina/http;
import ballerina/http_test_common as common;
import ballerina/test;
import ballerina/url;
import ballerina/http_test_common as common;

listener http:Listener QueryBindingEP = new (queryParamBindingTestPort, httpVersion = http:HTTP_1_1);
final http:Client queryBindingClient = check new ("http://localhost:" + queryParamBindingTestPort.toString(), httpVersion = http:HTTP_1_1);
Expand Down Expand Up @@ -167,8 +167,29 @@ service /queryparamservice on QueryBindingEP {
resource function get studentRest(StudentRest studentRest) returns StudentRest {
return studentRest;
}

resource function get queryAnnotation(@http:Query {name: "first"} string firstName, @http:Query {name: "last-name"} string lastName) returns string {
return string `Hello, ${firstName} ${lastName}`;
}

resource function get queryAnnotation/negative/q1(@http:Query {name: ""} string firstName) returns string {
return string `Hello, ${firstName}`;
}

resource function get queryAnnotation/negative/q2(@http:Query {name: ()} string lastName) returns string {
return string `Hello, ${lastName}`;
}

resource function get queryAnnotation/mapQueries(@http:Query {name: "rMq"} Mq mq) returns string {
return string `Hello, ${mq.name} ${mq.age}`;
}
}

public type Mq record {
string name;
int age;
};

service /default on QueryBindingEP {
resource function get checkstring(string foo = "hello") returns json {
json responseJson = {value1: foo};
Expand Down Expand Up @@ -353,7 +374,7 @@ function testNegativeStringQueryBindingCaseSensitivity() returns error? {
if response is http:Response {
test:assertEquals(response.statusCode, 400);
check common:assertJsonErrorPayload(check response.getJsonPayload(), "no query param value found for 'foo'",
"Bad Request", 400, "/queryparamservice/?FOO=WSO2&bar=go", "GET");
"Bad Request", 400, "/queryparamservice/?FOO=WSO2&bar=go", "GET");
} else {
test:assertFail(msg = "Found unexpected output type: " + response.message());
}
Expand All @@ -365,7 +386,7 @@ function testNegativeIntQueryBindingCastingError() returns error? {
if response is http:Response {
test:assertEquals(response.statusCode, 400);
check common:assertJsonErrorPayload(check response.getJsonPayload(), "error in casting query param : 'bar'",
"Bad Request", 400, "/queryparamservice/?foo=WSO2&bar=go", "GET");
"Bad Request", 400, "/queryparamservice/?foo=WSO2&bar=go", "GET");
} else {
test:assertFail(msg = "Found unexpected output type: " + response.message());
}
Expand All @@ -374,7 +395,7 @@ function testNegativeIntQueryBindingCastingError() returns error? {
if response is http:Response {
test:assertEquals(response.statusCode, 400);
check common:assertJsonErrorPayload(check response.getJsonPayload(), "error in casting query param : 'bar'",
"Bad Request", 400, "/queryparamservice/?foo=WSO2&bar=", "GET");
"Bad Request", 400, "/queryparamservice/?foo=WSO2&bar=", "GET");
} else {
test:assertFail(msg = "Found unexpected output type: " + response.message());
}
Expand Down Expand Up @@ -549,7 +570,7 @@ function testEmptyQueryParamBinding() returns error? {
if response is http:Response {
test:assertEquals(response.statusCode, 400);
check common:assertJsonErrorPayload(check response.getJsonPayload(), "no query param value found for 'x-Type'",
"Bad Request", 400, "/queryparamservice/q9?x-Type", "GET");
"Bad Request", 400, "/queryparamservice/q9?x-Type", "GET");
} else {
test:assertFail(msg = "Found unexpected output type: " + response.message());
}
Expand Down Expand Up @@ -736,3 +757,27 @@ function testMapOfJsonTypedQueryParamBinding3() returns error? {
response = check queryBindingClient->get("/queryparamservice/q13?obj=" + mapOfJsonsEncoded);
test:assertEquals(response.statusCode, 400, msg = "Found unexpected output");
}

@test:Config {}
function testforQueryParamterNameOverwrite() returns error? {
string result = check queryBindingClient->get("/queryparamservice/queryAnnotation?first=Harry&last-name=Potter");
test:assertEquals(result, "Hello, Harry Potter", msg = string `Found ${result}, expected Harry`);

map<json> mapOfJsons = {
name: "Ron",
age: 10
};
string mapOfQueries = check url:encode(mapOfJsons.toJsonString(), "UTF-8");

result = check queryBindingClient->get("/queryparamservice/queryAnnotation/mapQueries?rMq=" + mapOfQueries);
test:assertEquals(result, "Hello, Ron 10", msg = string `Found ${result}, expected Harry`);
}

@test:Config {}
function testforNegativeQueryParamterNameOverwrite() returns error? {
string result = check queryBindingClient->get("/queryparamservice/queryAnnotation/negative/q1?firstName=Harry");
test:assertEquals(result, "Hello, Harry");

result = check queryBindingClient->get("/queryparamservice/queryAnnotation/negative/q2?lastName=Anne");
test:assertEquals(result, "Hello, Anne");
}
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

- [Add `anydata` support for `setPayload` methods in the request and response objects](https://github.com/ballerina-platform/ballerina-library/issues/6954)
- [Improve `@http:Query` annotation to overwrite the query parameter name in client] (https://github.com/ballerina-platform/ballerina-library/issues/6983)
- [Improve `@http:Query` annotation to overwrite the query parameter name in service] (https://github.com/ballerina-platform/ballerina-library/issues/7006)

## [2.12.0] - 2024-08-20

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import static io.ballerina.stdlib.http.api.HttpConstants.ANN_NAME_CALLER_INFO;
import static io.ballerina.stdlib.http.api.HttpConstants.ANN_NAME_HEADER;
import static io.ballerina.stdlib.http.api.HttpConstants.ANN_NAME_PAYLOAD;
import static io.ballerina.stdlib.http.api.HttpConstants.ANN_NAME_QUERY;
import static io.ballerina.stdlib.http.api.HttpConstants.COLON;
import static io.ballerina.stdlib.http.api.HttpConstants.PROTOCOL_HTTP;
import static io.ballerina.stdlib.http.api.HttpUtil.getParameterTypes;
Expand Down Expand Up @@ -89,6 +90,7 @@ public class ParamHandler {
+ ANN_NAME_CALLER_INFO;
public static final String CACHE_ANNOTATION = ModuleUtils.getHttpPackageIdentifier() + COLON
+ ANN_NAME_CACHE;
public static final String QUERY_ANNOTATION = ModuleUtils.getHttpPackageIdentifier() + COLON + ANN_NAME_QUERY;

public ParamHandler(ResourceMethodType resource, int pathParamCount, boolean constraintValidation) {
this.resource = resource;
Expand Down Expand Up @@ -272,11 +274,28 @@ private void createHeaderParam(String paramName, BMap annotations) {

private void createQueryParam(int index, ResourceMethodType balResource, Type originalType) {
io.ballerina.runtime.api.types.Parameter parameter = balResource.getParameters()[index];
QueryParam queryParam = new QueryParam(originalType, HttpUtil.unescapeAndEncodeValue(parameter.name), index,
parameter.isDefault, constraintValidation);
String paramName = parameter.name;
BMap annotations = (BMap) balResource.getAnnotation(
StringUtils.fromString(PARAM_ANNOT_PREFIX + IdentifierUtils.escapeSpecialCharacters(paramName)));
if (annotations != null) {
String queryParamName = getQueryParamName(paramName, annotations);
paramName = queryParamName.isBlank() ? paramName : queryParamName;
}
paramName = HttpUtil.unescapeAndEncodeValue(paramName);
QueryParam queryParam = new QueryParam(originalType, paramName, index, parameter.isDefault,
constraintValidation);
this.queryParams.add(queryParam);
}

private static String getQueryParamName(String paramName, BMap annotations) {
BMap mapValue = annotations.getMapValue(StringUtils.fromString(QUERY_ANNOTATION));
Object queryName = mapValue.get(HttpConstants.ANN_FIELD_NAME);
if (queryName instanceof BString query) {
return query.getValue();
}
return paramName;
}

public boolean isPayloadBindingRequired() {
return payloadParam != null;
}
Expand Down
Loading