Skip to content

Commit d35b3d8

Browse files
tsp, update logic on encode and nullable (#2686)
* 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
1 parent e97080d commit d35b3d8

35 files changed

+1171
-232
lines changed

javagen/src/main/java/com/azure/autorest/template/ModelTemplate.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,17 @@ private void addModelConstructor(ClientModel model, JavaVisibility constructorVi
835835
// Finally, add all required properties.
836836
if (settings.isRequiredFieldsAsConstructorArgs()) {
837837
for (ClientModelProperty property : requiredProperties) {
838-
constructor.line("this." + property.getName() + " = " + property.getWireType().convertFromClientType(property.getName()) + ";");
838+
if (property.getClientType() != property.getWireType()) {
839+
// If the property needs to be converted and the passed value is null, set the field to null as the
840+
// converter will likely throw a NullPointerException.
841+
// Otherwise, just convert the value.
842+
constructor.ifBlock(property.getName() + " == null",
843+
ifBlock -> ifBlock.line("this.%s = %s;", property.getName(), property.getWireType().defaultValueExpression()))
844+
.elseBlock(elseBlock -> elseBlock.line("this.%s = %s;",
845+
property.getName(), property.getWireType().convertFromClientType(property.getName())));
846+
} else {
847+
constructor.line("this." + property.getName() + " = " + property.getWireType().convertFromClientType(property.getName()) + ";");
848+
}
839849
}
840850
}
841851
});
@@ -993,7 +1003,7 @@ private static void addSetterMethod(IType propertyWireType, IType propertyClient
9931003
// converter will likely throw a NullPointerException.
9941004
// Otherwise, just convert the value.
9951005
methodBlock.ifBlock(property.getName() + " == null",
996-
ifBlock -> ifBlock.line("this.%s = null;", property.getName()))
1006+
ifBlock -> ifBlock.line("this.%s = %s;", property.getName(), property.getWireType().defaultValueExpression()))
9971007
.elseBlock(elseBlock ->
9981008
elseBlock.line("this.%s = %s;", property.getName(), propertyWireType.convertFromClientType(expression)));
9991009
} else {

typespec-extension/changelog.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Release History
22

3+
## 0.15.8 (2024-04-11)
4+
5+
Compatible with compiler 0.55.
6+
7+
- Bug fix on `@encode`.
8+
- Bug fix on required property of nullable type.
9+
310
## 0.15.7 (2024-04-03)
411

512
Compatible with compiler 0.55.

typespec-extension/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

typespec-extension/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@azure-tools/typespec-java",
3-
"version": "0.15.7",
3+
"version": "0.15.8",
44
"description": "TypeSpec library for emitting Java client from the TypeSpec REST protocol binding",
55
"keywords": [
66
"TypeSpec"

typespec-extension/src/code-model-builder.ts

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2376,25 +2376,34 @@ export class CodeModelBuilder {
23762376
// }
23772377
const format = getFormat(this.program, prop);
23782378
if (format) {
2379+
// TODO: deprecate format
23792380
if (prop.type.kind === "Scalar" && schema instanceof StringSchema) {
23802381
schema = this.processFormatString(prop.type, format, nameHint);
23812382
}
2382-
} else if (prop.type.kind === "Scalar") {
2383+
} else {
2384+
// encode
23832385
const encode = getEncode(this.program, prop);
23842386
if (encode) {
2385-
if (encode.encoding === "seconds" && hasScalarAsBase(prop.type, "duration")) {
2386-
schema = this.processDurationSchema(prop.type, nameHint, getDurationFormat(encode));
2387-
} else if (
2388-
(encode.encoding === "rfc3339" || encode.encoding === "rfc7231" || encode.encoding === "unixTimestamp") &&
2389-
(hasScalarAsBase(prop.type, "utcDateTime") || hasScalarAsBase(prop.type, "offsetDateTime"))
2390-
) {
2391-
if (encode.encoding === "unixTimestamp") {
2392-
return this.processUnixTimeSchema(prop.type, nameHint);
2393-
} else {
2394-
return this.processDateTimeSchema(prop.type, nameHint, encode.encoding === "rfc7231");
2387+
let type = prop.type;
2388+
if (prop.type.kind === "Union" && isNullableType(prop.type)) {
2389+
const nonNullVariants = Array.from(prop.type.variants.values()).filter((it) => !isNullType(it.type));
2390+
type = nonNullVariants[0].type;
2391+
}
2392+
if (type && type.kind === "Scalar") {
2393+
if (encode.encoding === "seconds" && hasScalarAsBase(type, "duration")) {
2394+
schema = this.processDurationSchema(type, nameHint, getDurationFormat(encode));
2395+
} else if (
2396+
(encode.encoding === "rfc3339" || encode.encoding === "rfc7231" || encode.encoding === "unixTimestamp") &&
2397+
(hasScalarAsBase(type, "utcDateTime") || hasScalarAsBase(type, "offsetDateTime"))
2398+
) {
2399+
if (encode.encoding === "unixTimestamp") {
2400+
return this.processUnixTimeSchema(type, nameHint);
2401+
} else {
2402+
return this.processDateTimeSchema(type, nameHint, encode.encoding === "rfc7231");
2403+
}
2404+
} else if (encode.encoding === "base64url" && hasScalarAsBase(type, "bytes")) {
2405+
return this.processByteArraySchema(type, nameHint, true);
23952406
}
2396-
} else if (encode.encoding === "base64url" && hasScalarAsBase(prop.type, "bytes")) {
2397-
return this.processByteArraySchema(prop.type, nameHint, true);
23982407
}
23992408
}
24002409
}

typespec-tests/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
"testserver-run": "npx cadl-ranch serve ./node_modules/@azure-tools/cadl-ranch-specs/http --coverageFile ./cadl-ranch-coverage-java.json"
1010
},
1111
"dependencies": {
12-
"@azure-tools/cadl-ranch-specs": "0.31.6",
13-
"@azure-tools/typespec-java": "file:/../typespec-extension/azure-tools-typespec-java-0.15.7.tgz"
12+
"@azure-tools/cadl-ranch-specs": "0.31.7",
13+
"@azure-tools/typespec-java": "file:/../typespec-extension/azure-tools-typespec-java-0.15.8.tgz"
1414
},
1515
"devDependencies": {
1616
"@typespec/prettier-plugin-typespec": "~0.55.0",

typespec-tests/src/main/java/com/cadl/flatten/FlattenAsyncClient.java

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,19 @@
1818
import com.azure.core.util.FluxUtil;
1919
import com.cadl.flatten.implementation.FlattenClientImpl;
2020
import com.cadl.flatten.implementation.JsonMergePatchHelper;
21+
import com.cadl.flatten.implementation.MultipartFormDataHelper;
2122
import com.cadl.flatten.implementation.models.SendLongRequest;
2223
import com.cadl.flatten.implementation.models.SendProjectedNameRequest;
2324
import com.cadl.flatten.implementation.models.SendRequest;
25+
import com.cadl.flatten.implementation.models.UploadFileRequest;
26+
import com.cadl.flatten.implementation.models.UploadTodoRequest;
27+
import com.cadl.flatten.models.FileDataFileDetails;
2428
import com.cadl.flatten.models.SendLongOptions;
2529
import com.cadl.flatten.models.TodoItem;
2630
import com.cadl.flatten.models.UpdatePatchRequest;
31+
import com.cadl.flatten.models.UploadTodoOptions;
2732
import com.cadl.flatten.models.User;
33+
import java.util.Objects;
2834
import reactor.core.publisher.Mono;
2935

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

203+
/**
204+
* The uploadFile operation.
205+
*
206+
* @param name A sequence of textual characters.
207+
* @param request The request parameter.
208+
* @param requestOptions The options to configure the HTTP request before HTTP client sends it.
209+
* @throws HttpResponseException thrown if the request is rejected by server.
210+
* @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
211+
* @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
212+
* @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
213+
* @return the {@link Response} on successful completion of {@link Mono}.
214+
*/
215+
@Generated
216+
@ServiceMethod(returns = ReturnType.SINGLE)
217+
Mono<Response<Void>> uploadFileWithResponse(String name, BinaryData request, RequestOptions requestOptions) {
218+
// Protocol API requires serialization of parts with content-disposition and data, as operation 'uploadFile' is 'multipart/form-data'
219+
return this.serviceClient.uploadFileWithResponseAsync(name, request, requestOptions);
220+
}
221+
222+
/**
223+
* The uploadTodo operation.
224+
*
225+
* @param request The request parameter.
226+
* @param requestOptions The options to configure the HTTP request before HTTP client sends it.
227+
* @throws HttpResponseException thrown if the request is rejected by server.
228+
* @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
229+
* @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
230+
* @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
231+
* @return the {@link Response} on successful completion of {@link Mono}.
232+
*/
233+
@Generated
234+
@ServiceMethod(returns = ReturnType.SINGLE)
235+
Mono<Response<Void>> uploadTodoWithResponse(BinaryData request, RequestOptions requestOptions) {
236+
// Protocol API requires serialization of parts with content-disposition and data, as operation 'uploadTodo' is 'multipart/form-data'
237+
return this.serviceClient.uploadTodoWithResponseAsync(request, requestOptions);
238+
}
239+
197240
/**
198241
* The send operation.
199242
*
@@ -323,4 +366,68 @@ public Mono<TodoItem> update(long id, UpdatePatchRequest request) {
323366
return updateWithResponse(id, requestInBinaryData, requestOptions).flatMap(FluxUtil::toMono)
324367
.map(protocolMethodData -> protocolMethodData.toObject(TodoItem.class));
325368
}
369+
370+
/**
371+
* The uploadFile operation.
372+
*
373+
* @param name A sequence of textual characters.
374+
* @param fileData The file details for the "file_data" field.
375+
* @throws IllegalArgumentException thrown if parameters fail the validation.
376+
* @throws HttpResponseException thrown if the request is rejected by server.
377+
* @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
378+
* @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
379+
* @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
380+
* @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent.
381+
* @return A {@link Mono} that completes when a successful response is received.
382+
*/
383+
@Generated
384+
@ServiceMethod(returns = ReturnType.SINGLE)
385+
public Mono<Void> uploadFile(String name, FileDataFileDetails fileData) {
386+
// Generated convenience method for uploadFileWithResponse
387+
RequestOptions requestOptions = new RequestOptions();
388+
UploadFileRequest requestObj = new UploadFileRequest(fileData);
389+
BinaryData request = new MultipartFormDataHelper(requestOptions)
390+
.serializeFileField("file_data", requestObj.getFileData().getContent(),
391+
requestObj.getFileData().getContentType(), requestObj.getFileData().getFilename())
392+
.serializeTextField("constant", requestObj.getConstant())
393+
.end()
394+
.getRequestBody();
395+
return uploadFileWithResponse(name, request, requestOptions).flatMap(FluxUtil::toMono);
396+
}
397+
398+
/**
399+
* The uploadTodo operation.
400+
*
401+
* @param options Options for uploadTodo API.
402+
* @throws IllegalArgumentException thrown if parameters fail the validation.
403+
* @throws HttpResponseException thrown if the request is rejected by server.
404+
* @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
405+
* @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
406+
* @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
407+
* @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent.
408+
* @return A {@link Mono} that completes when a successful response is received.
409+
*/
410+
@Generated
411+
@ServiceMethod(returns = ReturnType.SINGLE)
412+
public Mono<Void> uploadTodo(UploadTodoOptions options) {
413+
// Generated convenience method for uploadTodoWithResponse
414+
RequestOptions requestOptions = new RequestOptions();
415+
UploadTodoRequest requestObj
416+
= new UploadTodoRequest(options.getTitle(), options.getStatus()).setDescription(options.getDescription())
417+
.setDummy(options.getDummy())
418+
.setProp1(options.getProp1())
419+
.setProp2(options.getProp2())
420+
.setProp3(options.getProp3());
421+
BinaryData request
422+
= new MultipartFormDataHelper(requestOptions).serializeTextField("title", requestObj.getTitle())
423+
.serializeTextField("description", requestObj.getDescription())
424+
.serializeTextField("status", Objects.toString(requestObj.getStatus()))
425+
.serializeTextField("_dummy", requestObj.getDummy())
426+
.serializeTextField("prop1", requestObj.getProp1())
427+
.serializeTextField("prop2", requestObj.getProp2())
428+
.serializeTextField("prop3", requestObj.getProp3())
429+
.end()
430+
.getRequestBody();
431+
return uploadTodoWithResponse(request, requestOptions).flatMap(FluxUtil::toMono);
432+
}
326433
}

0 commit comments

Comments
 (0)