Skip to content

Commit

Permalink
tsp, update logic on encode and nullable (#2686)
Browse files Browse the repository at this point in the history
* handle nullable - Union | null

* null guard in ctor on client to wire type conversion

* 0.15.8

* bug fix, when setting class var, it should use wireType
  • Loading branch information
weidongxu-microsoft authored Apr 11, 2024
1 parent e97080d commit d35b3d8
Show file tree
Hide file tree
Showing 35 changed files with 1,171 additions and 232 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -835,7 +835,17 @@ private void addModelConstructor(ClientModel model, JavaVisibility constructorVi
// Finally, add all required properties.
if (settings.isRequiredFieldsAsConstructorArgs()) {
for (ClientModelProperty property : requiredProperties) {
constructor.line("this." + property.getName() + " = " + property.getWireType().convertFromClientType(property.getName()) + ";");
if (property.getClientType() != property.getWireType()) {
// If the property needs to be converted and the passed value is null, set the field to null as the
// converter will likely throw a NullPointerException.
// Otherwise, just convert the value.
constructor.ifBlock(property.getName() + " == null",
ifBlock -> ifBlock.line("this.%s = %s;", property.getName(), property.getWireType().defaultValueExpression()))
.elseBlock(elseBlock -> elseBlock.line("this.%s = %s;",
property.getName(), property.getWireType().convertFromClientType(property.getName())));
} else {
constructor.line("this." + property.getName() + " = " + property.getWireType().convertFromClientType(property.getName()) + ";");
}
}
}
});
Expand Down Expand Up @@ -993,7 +1003,7 @@ private static void addSetterMethod(IType propertyWireType, IType propertyClient
// converter will likely throw a NullPointerException.
// Otherwise, just convert the value.
methodBlock.ifBlock(property.getName() + " == null",
ifBlock -> ifBlock.line("this.%s = null;", property.getName()))
ifBlock -> ifBlock.line("this.%s = %s;", property.getName(), property.getWireType().defaultValueExpression()))
.elseBlock(elseBlock ->
elseBlock.line("this.%s = %s;", property.getName(), propertyWireType.convertFromClientType(expression)));
} else {
Expand Down
7 changes: 7 additions & 0 deletions typespec-extension/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Release History

## 0.15.8 (2024-04-11)

Compatible with compiler 0.55.

- Bug fix on `@encode`.
- Bug fix on required property of nullable type.

## 0.15.7 (2024-04-03)

Compatible with compiler 0.55.
Expand Down
4 changes: 2 additions & 2 deletions typespec-extension/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion typespec-extension/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@azure-tools/typespec-java",
"version": "0.15.7",
"version": "0.15.8",
"description": "TypeSpec library for emitting Java client from the TypeSpec REST protocol binding",
"keywords": [
"TypeSpec"
Expand Down
35 changes: 22 additions & 13 deletions typespec-extension/src/code-model-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2376,25 +2376,34 @@ export class CodeModelBuilder {
// }
const format = getFormat(this.program, prop);
if (format) {
// TODO: deprecate format
if (prop.type.kind === "Scalar" && schema instanceof StringSchema) {
schema = this.processFormatString(prop.type, format, nameHint);
}
} else if (prop.type.kind === "Scalar") {
} else {
// encode
const encode = getEncode(this.program, prop);
if (encode) {
if (encode.encoding === "seconds" && hasScalarAsBase(prop.type, "duration")) {
schema = this.processDurationSchema(prop.type, nameHint, getDurationFormat(encode));
} else if (
(encode.encoding === "rfc3339" || encode.encoding === "rfc7231" || encode.encoding === "unixTimestamp") &&
(hasScalarAsBase(prop.type, "utcDateTime") || hasScalarAsBase(prop.type, "offsetDateTime"))
) {
if (encode.encoding === "unixTimestamp") {
return this.processUnixTimeSchema(prop.type, nameHint);
} else {
return this.processDateTimeSchema(prop.type, nameHint, encode.encoding === "rfc7231");
let type = prop.type;
if (prop.type.kind === "Union" && isNullableType(prop.type)) {
const nonNullVariants = Array.from(prop.type.variants.values()).filter((it) => !isNullType(it.type));
type = nonNullVariants[0].type;
}
if (type && type.kind === "Scalar") {
if (encode.encoding === "seconds" && hasScalarAsBase(type, "duration")) {
schema = this.processDurationSchema(type, nameHint, getDurationFormat(encode));
} else if (
(encode.encoding === "rfc3339" || encode.encoding === "rfc7231" || encode.encoding === "unixTimestamp") &&
(hasScalarAsBase(type, "utcDateTime") || hasScalarAsBase(type, "offsetDateTime"))
) {
if (encode.encoding === "unixTimestamp") {
return this.processUnixTimeSchema(type, nameHint);
} else {
return this.processDateTimeSchema(type, nameHint, encode.encoding === "rfc7231");
}
} else if (encode.encoding === "base64url" && hasScalarAsBase(type, "bytes")) {
return this.processByteArraySchema(type, nameHint, true);
}
} else if (encode.encoding === "base64url" && hasScalarAsBase(prop.type, "bytes")) {
return this.processByteArraySchema(prop.type, nameHint, true);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions typespec-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"testserver-run": "npx cadl-ranch serve ./node_modules/@azure-tools/cadl-ranch-specs/http --coverageFile ./cadl-ranch-coverage-java.json"
},
"dependencies": {
"@azure-tools/cadl-ranch-specs": "0.31.6",
"@azure-tools/typespec-java": "file:/../typespec-extension/azure-tools-typespec-java-0.15.7.tgz"
"@azure-tools/cadl-ranch-specs": "0.31.7",
"@azure-tools/typespec-java": "file:/../typespec-extension/azure-tools-typespec-java-0.15.8.tgz"
},
"devDependencies": {
"@typespec/prettier-plugin-typespec": "~0.55.0",
Expand Down
107 changes: 107 additions & 0 deletions typespec-tests/src/main/java/com/cadl/flatten/FlattenAsyncClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,19 @@
import com.azure.core.util.FluxUtil;
import com.cadl.flatten.implementation.FlattenClientImpl;
import com.cadl.flatten.implementation.JsonMergePatchHelper;
import com.cadl.flatten.implementation.MultipartFormDataHelper;
import com.cadl.flatten.implementation.models.SendLongRequest;
import com.cadl.flatten.implementation.models.SendProjectedNameRequest;
import com.cadl.flatten.implementation.models.SendRequest;
import com.cadl.flatten.implementation.models.UploadFileRequest;
import com.cadl.flatten.implementation.models.UploadTodoRequest;
import com.cadl.flatten.models.FileDataFileDetails;
import com.cadl.flatten.models.SendLongOptions;
import com.cadl.flatten.models.TodoItem;
import com.cadl.flatten.models.UpdatePatchRequest;
import com.cadl.flatten.models.UploadTodoOptions;
import com.cadl.flatten.models.User;
import java.util.Objects;
import reactor.core.publisher.Mono;

/**
Expand Down Expand Up @@ -194,6 +200,43 @@ public Mono<Response<BinaryData>> updateWithResponse(long id, BinaryData request
return this.serviceClient.updateWithResponseAsync(id, request, requestOptions);
}

/**
* The uploadFile operation.
*
* @param name A sequence of textual characters.
* @param request The request parameter.
* @param requestOptions The options to configure the HTTP request before HTTP client sends it.
* @throws HttpResponseException thrown if the request is rejected by server.
* @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
* @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
* @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
* @return the {@link Response} on successful completion of {@link Mono}.
*/
@Generated
@ServiceMethod(returns = ReturnType.SINGLE)
Mono<Response<Void>> uploadFileWithResponse(String name, BinaryData request, RequestOptions requestOptions) {
// Protocol API requires serialization of parts with content-disposition and data, as operation 'uploadFile' is 'multipart/form-data'
return this.serviceClient.uploadFileWithResponseAsync(name, request, requestOptions);
}

/**
* The uploadTodo operation.
*
* @param request The request parameter.
* @param requestOptions The options to configure the HTTP request before HTTP client sends it.
* @throws HttpResponseException thrown if the request is rejected by server.
* @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
* @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
* @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
* @return the {@link Response} on successful completion of {@link Mono}.
*/
@Generated
@ServiceMethod(returns = ReturnType.SINGLE)
Mono<Response<Void>> uploadTodoWithResponse(BinaryData request, RequestOptions requestOptions) {
// Protocol API requires serialization of parts with content-disposition and data, as operation 'uploadTodo' is 'multipart/form-data'
return this.serviceClient.uploadTodoWithResponseAsync(request, requestOptions);
}

/**
* The send operation.
*
Expand Down Expand Up @@ -323,4 +366,68 @@ public Mono<TodoItem> update(long id, UpdatePatchRequest request) {
return updateWithResponse(id, requestInBinaryData, requestOptions).flatMap(FluxUtil::toMono)
.map(protocolMethodData -> protocolMethodData.toObject(TodoItem.class));
}

/**
* The uploadFile operation.
*
* @param name A sequence of textual characters.
* @param fileData The file details for the "file_data" field.
* @throws IllegalArgumentException thrown if parameters fail the validation.
* @throws HttpResponseException thrown if the request is rejected by server.
* @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
* @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
* @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
* @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent.
* @return A {@link Mono} that completes when a successful response is received.
*/
@Generated
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono<Void> uploadFile(String name, FileDataFileDetails fileData) {
// Generated convenience method for uploadFileWithResponse
RequestOptions requestOptions = new RequestOptions();
UploadFileRequest requestObj = new UploadFileRequest(fileData);
BinaryData request = new MultipartFormDataHelper(requestOptions)
.serializeFileField("file_data", requestObj.getFileData().getContent(),
requestObj.getFileData().getContentType(), requestObj.getFileData().getFilename())
.serializeTextField("constant", requestObj.getConstant())
.end()
.getRequestBody();
return uploadFileWithResponse(name, request, requestOptions).flatMap(FluxUtil::toMono);
}

/**
* The uploadTodo operation.
*
* @param options Options for uploadTodo API.
* @throws IllegalArgumentException thrown if parameters fail the validation.
* @throws HttpResponseException thrown if the request is rejected by server.
* @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
* @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
* @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
* @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent.
* @return A {@link Mono} that completes when a successful response is received.
*/
@Generated
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono<Void> uploadTodo(UploadTodoOptions options) {
// Generated convenience method for uploadTodoWithResponse
RequestOptions requestOptions = new RequestOptions();
UploadTodoRequest requestObj
= new UploadTodoRequest(options.getTitle(), options.getStatus()).setDescription(options.getDescription())
.setDummy(options.getDummy())
.setProp1(options.getProp1())
.setProp2(options.getProp2())
.setProp3(options.getProp3());
BinaryData request
= new MultipartFormDataHelper(requestOptions).serializeTextField("title", requestObj.getTitle())
.serializeTextField("description", requestObj.getDescription())
.serializeTextField("status", Objects.toString(requestObj.getStatus()))
.serializeTextField("_dummy", requestObj.getDummy())
.serializeTextField("prop1", requestObj.getProp1())
.serializeTextField("prop2", requestObj.getProp2())
.serializeTextField("prop3", requestObj.getProp3())
.end()
.getRequestBody();
return uploadTodoWithResponse(request, requestOptions).flatMap(FluxUtil::toMono);
}
}
Loading

0 comments on commit d35b3d8

Please sign in to comment.