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

[BallerinaToOpenAPI]Fix mapping to application/x-www-form-urlencoded when it has response and request body in resources #926

Merged
merged 4 commits into from
Mar 29, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -23,17 +23,13 @@
import io.ballerina.compiler.api.symbols.TypeSymbol;
import io.ballerina.compiler.syntax.tree.AnnotationNode;
import io.ballerina.compiler.syntax.tree.ArrayTypeDescriptorNode;
import io.ballerina.compiler.syntax.tree.BasicLiteralNode;
import io.ballerina.compiler.syntax.tree.ExpressionNode;
import io.ballerina.compiler.syntax.tree.ListConstructorExpressionNode;
import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode;
import io.ballerina.compiler.syntax.tree.MappingFieldNode;
import io.ballerina.compiler.syntax.tree.Node;
import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode;
import io.ballerina.compiler.syntax.tree.RequiredParameterNode;
import io.ballerina.compiler.syntax.tree.SeparatedNodeList;
import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode;
import io.ballerina.compiler.syntax.tree.SpecificFieldNode;
import io.ballerina.compiler.syntax.tree.SyntaxKind;
import io.ballerina.compiler.syntax.tree.TypeDescriptorNode;
import io.ballerina.openapi.converter.Constants;
Expand All @@ -46,20 +42,23 @@
import io.swagger.v3.oas.models.media.StringSchema;
import io.swagger.v3.oas.models.parameters.RequestBody;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;

import javax.ws.rs.core.MediaType;

import static io.ballerina.openapi.converter.Constants.APPLICATION_PREFIX;
import static io.ballerina.openapi.converter.Constants.HTTP_PAYLOAD;
import static io.ballerina.openapi.converter.Constants.JSON_POSTFIX;
import static io.ballerina.openapi.converter.Constants.MEDIA_TYPE;
import static io.ballerina.openapi.converter.Constants.OCTECT_STREAM_POSTFIX;
import static io.ballerina.openapi.converter.Constants.TEXT_POSTFIX;
import static io.ballerina.openapi.converter.Constants.TEXT_PREFIX;
import static io.ballerina.openapi.converter.Constants.XML_POSTFIX;
import static io.ballerina.openapi.converter.Constants.X_WWW_FORM_URLENCODED_POSTFIX;
import static io.ballerina.openapi.converter.utils.ConverterCommonUtils.extractAnnotationFieldDetails;

/**
* OpenAPIRequestBodyMapper provides functionality for converting ballerina payload to OAS request body model.
Expand Down Expand Up @@ -165,8 +164,8 @@ private void handleSinglePayloadType(RequiredParameterNode payloadNode, Map<Stri
addConsumes(operationAdaptor, bodyParameter, mediaTypeString);
break;
case Constants.MAP_STRING:
mediaTypeString = customMediaPrefix == null ? MediaType.APPLICATION_FORM_URLENCODED :
APPLICATION_PREFIX + customMediaPrefix + X_WWW_FORM_URLENCODED_POSTFIX;
mediaTypeString = customMediaPrefix == null ? MediaType.APPLICATION_JSON :
APPLICATION_PREFIX + customMediaPrefix + JSON_POSTFIX;
Schema objectSchema = new ObjectSchema();
objectSchema.additionalProperties(new StringSchema());
io.swagger.v3.oas.models.media.MediaType mediaType = new io.swagger.v3.oas.models.media.MediaType();
Expand Down Expand Up @@ -242,31 +241,14 @@ private void handleArrayTypePayload(Map<String, Schema> schema, ArrayTypeDescrip
private void handleMultipleMIMETypes(RequestBody bodyParameter,
SeparatedNodeList<MappingFieldNode> fields,
RequiredParameterNode payloadNode, Map<String, Schema> schema) {
List<String> mimeTypes = new ArrayList<>();
for (AnnotationNode annotation : payloadNode.annotations()) {
mimeTypes = extractAnnotationFieldDetails(HTTP_PAYLOAD, MEDIA_TYPE, annotation, semanticModel);
NipunaRanasinghe marked this conversation as resolved.
Show resolved Hide resolved
}
RequestBody requestBody = new RequestBody();

for (MappingFieldNode fieldNode: fields) {
if (!(fieldNode instanceof SpecificFieldNode) ||
!((SpecificFieldNode) fieldNode).fieldName().toString().equals(MEDIA_TYPE) ||
fieldNode.children() == null) {
continue;
}
RequestBody requestBody = new RequestBody();
SpecificFieldNode field = (SpecificFieldNode) fieldNode;
if (field.valueExpr().isEmpty()) {
continue;
}
ExpressionNode valueNode = field.valueExpr().get();
if (valueNode instanceof ListConstructorExpressionNode) {
SeparatedNodeList mimeList = ((ListConstructorExpressionNode) valueNode).expressions();
for (Object mime : mimeList) {
if (mime instanceof BasicLiteralNode) {
createRequestBody(bodyParameter, payloadNode, schema, requestBody,
((BasicLiteralNode) mime).literalToken().text());
}
}
} else {
createRequestBody(bodyParameter, payloadNode, schema, requestBody,
valueNode.toString());
}
for (String mimeType: mimeTypes) {
createRequestBody(bodyParameter, payloadNode, schema, requestBody, mimeType);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode;
import io.ballerina.compiler.syntax.tree.FunctionSignatureNode;
import io.ballerina.compiler.syntax.tree.ListConstructorExpressionNode;
import io.ballerina.compiler.syntax.tree.MapTypeDescriptorNode;
import io.ballerina.compiler.syntax.tree.MappingFieldNode;
import io.ballerina.compiler.syntax.tree.Node;
import io.ballerina.compiler.syntax.tree.NodeList;
Expand Down Expand Up @@ -123,6 +124,7 @@
import static io.ballerina.openapi.converter.Constants.X_WWW_FORM_URLENCODED_POSTFIX;
import static io.ballerina.openapi.converter.utils.ConverterCommonUtils.extractAnnotationFieldDetails;
import static io.ballerina.openapi.converter.utils.ConverterCommonUtils.extractCustomMediaType;
import static io.ballerina.openapi.converter.utils.ConverterCommonUtils.getOpenApiSchema;

/**
* This class uses to map the Ballerina return details to the OAS response.
Expand Down Expand Up @@ -478,6 +480,19 @@ private Optional<ApiResponses> getAPIResponses(OperationAdaptor operationAdaptor
case OPTIONAL_TYPE_DESC:
return getAPIResponses(operationAdaptor, apiResponses,
((OptionalTypeDescriptorNode) typeNode).typeDescriptor(), customMediaPrefix, headers);
case MAP_TYPE_DESC:
apiResponse = setCacheHeader(headers, apiResponse, HTTP_200);
NipunaRanasinghe marked this conversation as resolved.
Show resolved Hide resolved
MapTypeDescriptorNode mapNode = (MapTypeDescriptorNode) typeNode;
ObjectSchema objectSchema = new ObjectSchema();
Schema<?> apiSchema = getOpenApiSchema(mapNode.mapTypeParamsNode().typeNode().kind());
objectSchema.additionalProperties(apiSchema);
mediaType.setSchema(objectSchema);
mediaTypeString = customMediaPrefix.isPresent() ? APPLICATION_PREFIX + customMediaPrefix.get() +
JSON_POSTFIX : MediaType.APPLICATION_JSON;
apiResponse.content(new Content().addMediaType(mediaTypeString, mediaType));
apiResponse.description(HTTP_200_DESCRIPTION);
apiResponses.put(HTTP_200, apiResponse);
return Optional.of(apiResponses);
default:
DiagnosticMessages errorMessage = DiagnosticMessages.OAS_CONVERTOR_101;
IncompatibleResourceDiagnostic error = new IncompatibleResourceDiagnostic(errorMessage, this.location,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ paths:
operationId: operation_post_/pets
requestBody:
content:
application/x-www-form-urlencoded:
application/json:
schema:
type: object
additionalProperties:
Expand All @@ -28,7 +28,7 @@ paths:
operationId: operation_post_/pets02
requestBody:
content:
application/x-www-form-urlencoded:
application/json:
schema:
type: object
additionalProperties:
Expand All @@ -49,4 +49,17 @@ paths:
responses:
"200":
description: Ok
/pets04:
post:
operationId: operation_post_/pets04
requestBody:
content:
application/x-www-form-urlencoded:
schema:
type: object
additionalProperties:
type: string
responses:
"200":
description: Ok
components: {}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,41 @@ paths:
type: object
additionalProperties:
type: string
/foo:
get:
operationId: operation_get_/foo
responses:
"200":
description: Ok
content:
application/x-www-form-urlencoded:
schema:
type: object
additionalProperties:
type: string
/baa:
get:
operationId: operation_get_/baa
lnash94 marked this conversation as resolved.
Show resolved Hide resolved
responses:
"200":
description: Ok
content:
application/json:
schema:
type: object
additionalProperties:
type: string
/baaint:
get:
operationId: operation_get_/baaint
lnash94 marked this conversation as resolved.
Show resolved Hide resolved
responses:
"200":
description: Ok
content:
application/json:
schema:
type: object
additionalProperties:
type: integer
format: int64
components: {}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ballerina/http;
import ballerina/mime;

listener http:Listener helloEp = new (9090);

Expand All @@ -17,4 +18,10 @@ service /payloadV on helloEp {
http:Ok ok = {body: ()};
return ok;
}

resource function post pets04(@http:Payload{mediaType: mime:APPLICATION_FORM_URLENCODED} map<string> req) returns
http:Ok {
http:Ok ok = {body: ()};
return ok;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,25 @@ service /payloadV on helloEp {
}
};
}
resource function get foo() returns @http:Payload {mediaType: mime:APPLICATION_FORM_URLENCODED} map<string> {
map<string> ms = {
"x": "abc",
"y": "cdf"
};
return ms;
}
resource function get baa() returns @http:Payload map<string> {
lnash94 marked this conversation as resolved.
Show resolved Hide resolved
map<string> ms = {
"x": "abc",
"y": "cdf"
};
return ms;
}
resource function get baaint() returns @http:Payload map<int> {
lnash94 marked this conversation as resolved.
Show resolved Hide resolved
map<int> ms = {
"x": 1,
"y": 2
};
return ms;
}
}