From 838c847d6cf1d084ffc5123f3db2ee6f5023a05a Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Thu, 28 Aug 2025 10:33:11 +0800 Subject: [PATCH] feat: support file delete api (#45) --- .../java/ai/z/openapi/AbstractAiClient.java | 5 ++- .../java/ai/z/openapi/api/file/FileApi.java | 9 ----- .../service/AbstractClientBaseService.java | 38 +++++++++++++++++-- .../openapi/service/file/FileDelRequest.java | 24 ++++++++++++ .../openapi/service/file/FileDelResponse.java | 6 ++- .../z/openapi/service/file/FileService.java | 7 ++++ .../openapi/service/file/FileServiceImpl.java | 6 +++ .../ai/z/openapi/service/model/ZAiError.java | 6 +-- .../service/model/ZAiHttpException.java | 18 +++++---- .../openapi/service/file/FileServiceTest.java | 20 ++++++++++ 10 files changed, 113 insertions(+), 26 deletions(-) create mode 100644 core/src/main/java/ai/z/openapi/service/file/FileDelRequest.java diff --git a/core/src/main/java/ai/z/openapi/AbstractAiClient.java b/core/src/main/java/ai/z/openapi/AbstractAiClient.java index 445ca58..9a819cd 100644 --- a/core/src/main/java/ai/z/openapi/AbstractAiClient.java +++ b/core/src/main/java/ai/z/openapi/AbstractAiClient.java @@ -33,6 +33,7 @@ import ai.z.openapi.core.model.FlowableClientResponse; import ai.z.openapi.utils.FlowableRequestSupplier; import ai.z.openapi.utils.OkHttps; import ai.z.openapi.utils.RequestSupplier; +import ai.z.openapi.utils.StringUtils; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.core.Single; import okhttp3.OkHttpClient; @@ -318,7 +319,9 @@ public abstract class AbstractAiClient extends AbstractClientBaseService { tResp.setMsg("Call Failed"); tResp.setSuccess(false); ChatError chatError = new ChatError(); - chatError.setCode(Integer.parseInt(e.code)); + if (StringUtils.isNotEmpty(e.code)) { + chatError.setCode(Integer.parseInt(e.code)); + } chatError.setMessage(e.getMessage()); tResp.setError(chatError); } diff --git a/core/src/main/java/ai/z/openapi/api/file/FileApi.java b/core/src/main/java/ai/z/openapi/api/file/FileApi.java index fb2ef34..b964090 100644 --- a/core/src/main/java/ai/z/openapi/api/file/FileApi.java +++ b/core/src/main/java/ai/z/openapi/api/file/FileApi.java @@ -31,15 +31,6 @@ public interface FileApi { @POST("files") Single uploadFile(@Body MultipartBody multipartBody); - /** - * Retrieve file metadata and information Gets detailed information about a previously - * uploaded file - * @param fileId Unique identifier of the file to retrieve - * @return File metadata including name, size, purpose, and creation time - */ - @GET("files/{file_id}") - Single retrieveFile(@Path("file_id") String fileId); - /** * Delete a file from the platform Permanently removes the file and all associated * data diff --git a/core/src/main/java/ai/z/openapi/service/AbstractClientBaseService.java b/core/src/main/java/ai/z/openapi/service/AbstractClientBaseService.java index 210e84e..4a24213 100644 --- a/core/src/main/java/ai/z/openapi/service/AbstractClientBaseService.java +++ b/core/src/main/java/ai/z/openapi/service/AbstractClientBaseService.java @@ -12,6 +12,7 @@ import ai.z.openapi.service.model.ZAiHttpException; import ai.z.openapi.utils.FlowableRequestSupplier; import ai.z.openapi.utils.RequestSupplier; import ai.z.openapi.utils.StringUtils; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import io.reactivex.rxjava3.core.BackpressureStrategy; import io.reactivex.rxjava3.core.Flowable; @@ -117,10 +118,41 @@ public abstract class AbstractClientBaseService { if (StringUtils.isEmpty(errorBody)) { throw e; } - ZAiError error = mapper.readValue(errorBody, ZAiError.class); - throw new ZAiHttpException(error, e, e.code()); + // here not only the ZAiError, also has {"error": "message"} and others + JsonNode jsonNode = mapper.readTree(errorBody); + if (jsonNode.has("error")) { + JsonNode errorNode = jsonNode.get("error"); + if (errorNode.isTextual()) { + JsonNode codeNode = jsonNode.get("code"); + throw new ZAiHttpException(errorNode.asText(), codeNode == null ? null : codeNode.asText(), e, + e.code()); + } + else { + ZAiError error = mapper.readValue(errorBody, ZAiError.class); + throw new ZAiHttpException(error, e, e.code()); + } + } + else if (jsonNode.has("msg")) { + JsonNode msgNode = jsonNode.get("msg"); + JsonNode codeNode = jsonNode.get("code"); + throw new ZAiHttpException(msgNode.asText(), codeNode == null ? null : codeNode.asText(), e, + e.code()); + } + else if (jsonNode.has("message")) { + JsonNode msgNode = jsonNode.get("message"); + JsonNode codeNode = jsonNode.get("code"); + throw new ZAiHttpException(msgNode.asText(), codeNode == null ? null : codeNode.asText(), e, + e.code()); + } + else { + throw new ZAiHttpException(errorBody, null, e, e.code()); + } } - catch (IOException ex) { + catch (ZAiHttpException zAiHttpException) { + throw zAiHttpException; + } + catch (Exception ex) { + logger.error(ex.getMessage(), ex); // couldn't parse ZAiError error throw e; } diff --git a/core/src/main/java/ai/z/openapi/service/file/FileDelRequest.java b/core/src/main/java/ai/z/openapi/service/file/FileDelRequest.java new file mode 100644 index 0000000..4e09cd0 --- /dev/null +++ b/core/src/main/java/ai/z/openapi/service/file/FileDelRequest.java @@ -0,0 +1,24 @@ +package ai.z.openapi.service.file; + +import ai.z.openapi.core.model.ClientRequest; +import ai.z.openapi.core.model.ClientResponse; +import ai.z.openapi.service.batches.BatchRequest; +import ai.z.openapi.service.model.ChatError; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@EqualsAndHashCode(callSuper = false) +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Data +@Builder +public class FileDelRequest implements ClientRequest { + + private String fileId; + +} diff --git a/core/src/main/java/ai/z/openapi/service/file/FileDelResponse.java b/core/src/main/java/ai/z/openapi/service/file/FileDelResponse.java index 0e21855..73db6c3 100644 --- a/core/src/main/java/ai/z/openapi/service/file/FileDelResponse.java +++ b/core/src/main/java/ai/z/openapi/service/file/FileDelResponse.java @@ -1,9 +1,11 @@ package ai.z.openapi.service.file; +import ai.z.openapi.core.model.ClientResponse; +import ai.z.openapi.service.model.ChatError; import lombok.Data; @Data -public class FileDelResponse { +public class FileDelResponse implements ClientResponse { private int code; @@ -13,4 +15,6 @@ public class FileDelResponse { private FileDeleted data; + private ChatError error; + } diff --git a/core/src/main/java/ai/z/openapi/service/file/FileService.java b/core/src/main/java/ai/z/openapi/service/file/FileService.java index a680c11..684a92c 100644 --- a/core/src/main/java/ai/z/openapi/service/file/FileService.java +++ b/core/src/main/java/ai/z/openapi/service/file/FileService.java @@ -15,6 +15,13 @@ public interface FileService { */ FileApiResponse uploadFile(FileUploadParams request); + /** + * Delete file by file ID. + * @param request the file id to delete. + * @return FileDelResponse containing the delete result + */ + FileDelResponse deleteFile(FileDelRequest request); + /** * Lists all files. * @param queryFilesRequest FileListParams containing the query parameters for listing diff --git a/core/src/main/java/ai/z/openapi/service/file/FileServiceImpl.java b/core/src/main/java/ai/z/openapi/service/file/FileServiceImpl.java index 1aa0d8f..fdaa2a8 100644 --- a/core/src/main/java/ai/z/openapi/service/file/FileServiceImpl.java +++ b/core/src/main/java/ai/z/openapi/service/file/FileServiceImpl.java @@ -70,6 +70,12 @@ public class FileServiceImpl implements FileService { return this.zAiClient.executeRequest(request, supplier, FileApiResponse.class); } + @Override + public FileDelResponse deleteFile(FileDelRequest request) { + RequestSupplier supplier = (params -> fileApi.deletedFile(params.getFileId())); + return this.zAiClient.executeRequest(request, supplier, FileDelResponse.class); + } + @Override public QueryFileApiResponse listFiles(FileListParams queryFilesRequest) { RequestSupplier supplier = (params) -> fileApi.queryFileList(params.getAfter(), diff --git a/core/src/main/java/ai/z/openapi/service/model/ZAiError.java b/core/src/main/java/ai/z/openapi/service/model/ZAiError.java index 16201c7..cd49534 100644 --- a/core/src/main/java/ai/z/openapi/service/model/ZAiError.java +++ b/core/src/main/java/ai/z/openapi/service/model/ZAiError.java @@ -30,12 +30,8 @@ public class ZAiError { */ String message; - String type; - - String param; - /** - * ZAI error code, for example "invalid_api_key" + * ZAI error code, for example "401" */ String code; diff --git a/core/src/main/java/ai/z/openapi/service/model/ZAiHttpException.java b/core/src/main/java/ai/z/openapi/service/model/ZAiHttpException.java index 0115242..c5dfc83 100644 --- a/core/src/main/java/ai/z/openapi/service/model/ZAiHttpException.java +++ b/core/src/main/java/ai/z/openapi/service/model/ZAiHttpException.java @@ -8,23 +8,27 @@ public class ZAiHttpException extends RuntimeException { public final int statusCode; /** - * ZAI error code, for example "invalid_api_key" + * ZAI error code, for example "1233" */ public final String code; - public final String param; - /** - * ZAI error type, for example "invalid_request_error" + * ZAI error message */ - public final String type; + public final String msg; public ZAiHttpException(ZAiError error, Exception parent, int statusCode) { super(error.error.message, parent); this.statusCode = statusCode; this.code = error.error.code; - this.param = error.error.param; - this.type = error.error.type; + this.msg = error.error.message; + } + + public ZAiHttpException(String errorMsg, String errorCode, Exception parent, int statusCode) { + super(errorMsg, parent); + this.statusCode = statusCode; + this.code = errorCode; + this.msg = errorMsg; } } diff --git a/core/src/test/java/ai/z/openapi/service/file/FileServiceTest.java b/core/src/test/java/ai/z/openapi/service/file/FileServiceTest.java index a18dd46..4f35345 100644 --- a/core/src/test/java/ai/z/openapi/service/file/FileServiceTest.java +++ b/core/src/test/java/ai/z/openapi/service/file/FileServiceTest.java @@ -174,6 +174,26 @@ public class FileServiceTest { logger.info("List files response: {}", mapper.writeValueAsString(response)); } + @Test + @DisplayName("Test Delete File - Basic Functionality") + @EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$") + void testDeleteFile() throws JsonProcessingException { + + String fileId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis()); + FileDelRequest request = FileDelRequest.builder().fileId(fileId).build(); + + // Execute test + FileDelResponse response = fileService.deleteFile(request); + + // Verify results + assertNotNull(response, "Response should not be null"); + assertFalse(response.isSuccess(), "Response should be failed"); + assertNull(response.getData(), "Response data should not be null"); + assertNotNull(response.getError(), "Response error should be null"); + + logger.info("Delete file response: {}", mapper.writeValueAsString(response)); + } + @Test @DisplayName("Test List Files with Purpose Filter") @EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")