diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/RESTCatalog.java b/paimon-core/src/main/java/org/apache/paimon/rest/RESTCatalog.java index bd0447c40a69..4cb5087e8340 100644 --- a/paimon-core/src/main/java/org/apache/paimon/rest/RESTCatalog.java +++ b/paimon-core/src/main/java/org/apache/paimon/rest/RESTCatalog.java @@ -57,7 +57,7 @@ import org.apache.paimon.rest.responses.ListDatabasesResponse; import org.apache.paimon.rest.responses.ListPartitionsResponse; import org.apache.paimon.rest.responses.ListTablesResponse; -import org.apache.paimon.rest.responses.SuccessResponse; +import org.apache.paimon.rest.responses.PartitionResponse; import org.apache.paimon.schema.Schema; import org.apache.paimon.schema.SchemaChange; import org.apache.paimon.schema.TableSchema; @@ -93,7 +93,6 @@ import static org.apache.paimon.catalog.CatalogUtils.checkNotSystemTable; import static org.apache.paimon.catalog.CatalogUtils.isSystemDatabase; import static org.apache.paimon.options.CatalogOptions.CASE_SENSITIVE; -import static org.apache.paimon.rest.RESTCatalogOptions.METASTORE_PARTITIONED; import static org.apache.paimon.utils.InternalRowPartitionComputer.convertSpecToInternalRow; import static org.apache.paimon.utils.Preconditions.checkNotNull; import static org.apache.paimon.utils.ThreadPoolUtils.createScheduledThreadPool; @@ -382,7 +381,7 @@ public void createPartition(Identifier identifier, Map partition resourcePaths.partitions( identifier.getDatabaseName(), identifier.getTableName()), request, - SuccessResponse.class, + PartitionResponse.class, headers()); } catch (NoSuchResourceException e) { throw new TableNotExistException(identifier); @@ -397,17 +396,14 @@ public void dropPartition(Identifier identifier, Map partitions) checkNotSystemTable(identifier, "dropPartition"); dropPartitionMetadata(identifier, partitions); Table table = getTable(identifier); - if (table != null) { - cleanPartitionsInFileSystem(table, partitions); - } else { - throw new TableNotExistException(identifier); - } + cleanPartitionsInFileSystem(table, partitions); } @Override public List listPartitions(Identifier identifier) throws TableNotExistException { - boolean whetherSupportListPartitions = context.options().get(METASTORE_PARTITIONED); + boolean whetherSupportListPartitions = + context.options().get(CoreOptions.METASTORE_PARTITIONED_TABLE); if (whetherSupportListPartitions) { FileStoreTable table = (FileStoreTable) getTable(identifier); RowType rowType = table.schema().logicalPartitionType(); @@ -489,8 +485,7 @@ public List listPartitionsFromServer(Identifier identifier, RowT } @VisibleForTesting - PartitionEntry convertToPartitionEntry( - ListPartitionsResponse.Partition partition, RowType rowType) { + PartitionEntry convertToPartitionEntry(PartitionResponse partition, RowType rowType) { InternalRowSerializer serializer = new InternalRowSerializer(rowType); GenericRow row = convertSpecToInternalRow(partition.getSpec(), rowType, null); return new PartitionEntry( @@ -528,18 +523,18 @@ protected GetTableResponse getTableResponse(Identifier identifier) } } - protected SuccessResponse dropPartitionMetadata( - Identifier identifier, Map partitions) + protected boolean dropPartitionMetadata(Identifier identifier, Map partitions) throws TableNoPermissionException { try { DropPartitionRequest request = new DropPartitionRequest(partitions); - return client.delete( + client.delete( resourcePaths.partitions( identifier.getDatabaseName(), identifier.getTableName()), request, headers()); + return true; } catch (NoSuchResourceException ignore) { - return new SuccessResponse(); + return true; } catch (ForbiddenException e) { throw new TableNoPermissionException(identifier, e); } diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/RESTCatalogOptions.java b/paimon-core/src/main/java/org/apache/paimon/rest/RESTCatalogOptions.java index d750654f94af..1af64def4f71 100644 --- a/paimon-core/src/main/java/org/apache/paimon/rest/RESTCatalogOptions.java +++ b/paimon-core/src/main/java/org/apache/paimon/rest/RESTCatalogOptions.java @@ -71,10 +71,4 @@ public class RESTCatalogOptions { .stringType() .noDefaultValue() .withDescription("REST Catalog auth token provider path."); - - public static final ConfigOption METASTORE_PARTITIONED = - ConfigOptions.key("metastore-partitioned") - .booleanType() - .defaultValue(false) - .withDescription("REST Catalog Server whether support list partitions."); } diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/responses/ListPartitionsResponse.java b/paimon-core/src/main/java/org/apache/paimon/rest/responses/ListPartitionsResponse.java index e2ce322fd7e3..1f194d208e99 100644 --- a/paimon-core/src/main/java/org/apache/paimon/rest/responses/ListPartitionsResponse.java +++ b/paimon-core/src/main/java/org/apache/paimon/rest/responses/ListPartitionsResponse.java @@ -26,7 +26,6 @@ import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; -import java.util.Map; /** Response for listing partitions. */ @JsonIgnoreProperties(ignoreUnknown = true) @@ -35,80 +34,16 @@ public class ListPartitionsResponse implements RESTResponse { public static final String FIELD_PARTITIONS = "partitions"; @JsonProperty(FIELD_PARTITIONS) - private final List partitions; + private final List partitions; @JsonCreator - public ListPartitionsResponse(@JsonProperty(FIELD_PARTITIONS) List partitions) { + public ListPartitionsResponse( + @JsonProperty(FIELD_PARTITIONS) List partitions) { this.partitions = partitions; } @JsonGetter(FIELD_PARTITIONS) - public List getPartitions() { + public List getPartitions() { return partitions; } - - /** Partition for rest api. */ - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Partition implements RESTResponse { - - private static final String FIELD_SPEC = "spec"; - public static final String FIELD_RECORD_COUNT = "recordCount"; - public static final String FIELD_FILE_SIZE_IN_BYTES = "fileSizeInBytes"; - public static final String FIELD_FILE_COUNT = "fileCount"; - public static final String FIELD_LAST_FILE_CREATION_TIME = "lastFileCreationTime"; - - @JsonProperty(FIELD_SPEC) - private final Map spec; - - @JsonProperty(FIELD_RECORD_COUNT) - private final long recordCount; - - @JsonProperty(FIELD_FILE_SIZE_IN_BYTES) - private final long fileSizeInBytes; - - @JsonProperty(FIELD_FILE_COUNT) - private final long fileCount; - - @JsonProperty(FIELD_LAST_FILE_CREATION_TIME) - private final long lastFileCreationTime; - - @JsonCreator - public Partition( - @JsonProperty(FIELD_SPEC) Map spec, - @JsonProperty(FIELD_RECORD_COUNT) long recordCount, - @JsonProperty(FIELD_FILE_SIZE_IN_BYTES) long fileSizeInBytes, - @JsonProperty(FIELD_FILE_COUNT) long fileCount, - @JsonProperty(FIELD_LAST_FILE_CREATION_TIME) long lastFileCreationTime) { - this.spec = spec; - this.recordCount = recordCount; - this.fileSizeInBytes = fileSizeInBytes; - this.fileCount = fileCount; - this.lastFileCreationTime = lastFileCreationTime; - } - - @JsonGetter(FIELD_SPEC) - public Map getSpec() { - return spec; - } - - @JsonGetter(FIELD_RECORD_COUNT) - public long getRecordCount() { - return recordCount; - } - - @JsonGetter(FIELD_FILE_SIZE_IN_BYTES) - public long getFileSizeInBytes() { - return fileSizeInBytes; - } - - @JsonGetter(FIELD_FILE_COUNT) - public long getFileCount() { - return fileCount; - } - - @JsonGetter(FIELD_LAST_FILE_CREATION_TIME) - public long getLastFileCreationTime() { - return lastFileCreationTime; - } - } } diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/responses/PartitionResponse.java b/paimon-core/src/main/java/org/apache/paimon/rest/responses/PartitionResponse.java new file mode 100644 index 000000000000..5c08c4d64f1c --- /dev/null +++ b/paimon-core/src/main/java/org/apache/paimon/rest/responses/PartitionResponse.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.paimon.rest.responses; + +import org.apache.paimon.rest.RESTResponse; + +import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonCreator; +import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonGetter; +import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Map; + +/** Partition for rest api. */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class PartitionResponse implements RESTResponse { + public static final String FIELD_SPEC = "spec"; + public static final String FIELD_RECORD_COUNT = "recordCount"; + public static final String FIELD_FILE_SIZE_IN_BYTES = "fileSizeInBytes"; + public static final String FIELD_FILE_COUNT = "fileCount"; + public static final String FIELD_LAST_FILE_CREATION_TIME = "lastFileCreationTime"; + + @JsonProperty(FIELD_SPEC) + private final Map spec; + + @JsonProperty(FIELD_RECORD_COUNT) + private final long recordCount; + + @JsonProperty(FIELD_FILE_SIZE_IN_BYTES) + private final long fileSizeInBytes; + + @JsonProperty(FIELD_FILE_COUNT) + private final long fileCount; + + @JsonProperty(FIELD_LAST_FILE_CREATION_TIME) + private final long lastFileCreationTime; + + @JsonCreator + public PartitionResponse( + @JsonProperty(FIELD_SPEC) Map spec, + @JsonProperty(FIELD_RECORD_COUNT) long recordCount, + @JsonProperty(FIELD_FILE_SIZE_IN_BYTES) long fileSizeInBytes, + @JsonProperty(FIELD_FILE_COUNT) long fileCount, + @JsonProperty(FIELD_LAST_FILE_CREATION_TIME) long lastFileCreationTime) { + this.spec = spec; + this.recordCount = recordCount; + this.fileSizeInBytes = fileSizeInBytes; + this.fileCount = fileCount; + this.lastFileCreationTime = lastFileCreationTime; + } + + @JsonGetter(FIELD_SPEC) + public Map getSpec() { + return spec; + } + + @JsonGetter(FIELD_RECORD_COUNT) + public long getRecordCount() { + return recordCount; + } + + @JsonGetter(FIELD_FILE_SIZE_IN_BYTES) + public long getFileSizeInBytes() { + return fileSizeInBytes; + } + + @JsonGetter(FIELD_FILE_COUNT) + public long getFileCount() { + return fileCount; + } + + @JsonGetter(FIELD_LAST_FILE_CREATION_TIME) + public long getLastFileCreationTime() { + return lastFileCreationTime; + } +} diff --git a/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTMessage.java b/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTMessage.java index 33f8451f3f6b..592ec90c3bf4 100644 --- a/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTMessage.java +++ b/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTMessage.java @@ -34,6 +34,7 @@ import org.apache.paimon.rest.responses.ListDatabasesResponse; import org.apache.paimon.rest.responses.ListPartitionsResponse; import org.apache.paimon.rest.responses.ListTablesResponse; +import org.apache.paimon.rest.responses.PartitionResponse; import org.apache.paimon.schema.Schema; import org.apache.paimon.schema.SchemaChange; import org.apache.paimon.types.DataField; @@ -147,8 +148,7 @@ public static DropPartitionRequest dropPartitionRequest() { public static ListPartitionsResponse listPartitionsResponse() { Map spec = new HashMap<>(); spec.put("f0", "1"); - ListPartitionsResponse.Partition partition = - new ListPartitionsResponse.Partition(spec, 1, 1, 1, 1); + PartitionResponse partition = new PartitionResponse(spec, 1, 1, 1, 1); return new ListPartitionsResponse(ImmutableList.of(partition)); } diff --git a/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogTest.java b/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogTest.java index b52d81e8e7cd..2104d36d9d97 100644 --- a/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogTest.java +++ b/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogTest.java @@ -18,6 +18,7 @@ package org.apache.paimon.rest; +import org.apache.paimon.CoreOptions; import org.apache.paimon.catalog.Catalog; import org.apache.paimon.catalog.CatalogContext; import org.apache.paimon.catalog.Database; @@ -34,6 +35,7 @@ import org.apache.paimon.rest.responses.ListDatabasesResponse; import org.apache.paimon.rest.responses.ListPartitionsResponse; import org.apache.paimon.rest.responses.ListTablesResponse; +import org.apache.paimon.rest.responses.PartitionResponse; import org.apache.paimon.rest.responses.SuccessResponse; import org.apache.paimon.schema.SchemaChange; import org.apache.paimon.table.Table; @@ -472,7 +474,7 @@ public void testDropPartitionWhenTableNoExist() throws Exception { @Test public void testListPartitionsWhenMetastorePartitionedIsTrue() throws Exception { Options options = mockInitOptions(); - options = options.set(RESTCatalogOptions.METASTORE_PARTITIONED, true); + options = options.set(CoreOptions.METASTORE_PARTITIONED_TABLE, true); mockConfig(warehouseStr); RESTCatalog restCatalog = new RESTCatalog(CatalogContext.create(options)); RESTCatalog mockRestCatalog = spy(restCatalog); @@ -506,8 +508,7 @@ public void convertToPartitionEntryTest() { fields.add(new DataField(0, "a", DataTypes.INT())); fields.add(new DataField(1, "b", DataTypes.STRING())); RowType partitionRowType = new RowType(false, fields); - ListPartitionsResponse.Partition partition = - new ListPartitionsResponse.Partition(spec, 1, 1, 1, 1); + PartitionResponse partition = new PartitionResponse(spec, 1, 1, 1, 1); PartitionEntry partitionEntry = mockRestCatalog.convertToPartitionEntry(partition, partitionRowType); InternalRowPartitionComputer partitionComputer = diff --git a/paimon-open-api/rest-catalog-open-api.yaml b/paimon-open-api/rest-catalog-open-api.yaml index 9664dcb41f9a..b1c6489855ab 100644 --- a/paimon-open-api/rest-catalog-open-api.yaml +++ b/paimon-open-api/rest-catalog-open-api.yaml @@ -471,7 +471,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/SuccessResponse' + $ref: '#/components/schemas/PartitionResponse' "404": description: Resource not found content: @@ -508,11 +508,7 @@ paths: $ref: '#/components/schemas/DropPartitionRequest' responses: "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/SuccessResponse' + description: Success, no content "404": description: Resource not found content: @@ -880,8 +876,8 @@ components: partitions: type: array items: - $ref: '#/components/schemas/Partition' - Partition: + $ref: '#/components/schemas/PartitionResponse' + PartitionResponse: type: object properties: spec: diff --git a/paimon-open-api/src/main/java/org/apache/paimon/open/api/RESTCatalogController.java b/paimon-open-api/src/main/java/org/apache/paimon/open/api/RESTCatalogController.java index b09c352099ba..87443d1d6245 100644 --- a/paimon-open-api/src/main/java/org/apache/paimon/open/api/RESTCatalogController.java +++ b/paimon-open-api/src/main/java/org/apache/paimon/open/api/RESTCatalogController.java @@ -35,7 +35,7 @@ import org.apache.paimon.rest.responses.ListDatabasesResponse; import org.apache.paimon.rest.responses.ListPartitionsResponse; import org.apache.paimon.rest.responses.ListTablesResponse; -import org.apache.paimon.rest.responses.SuccessResponse; +import org.apache.paimon.rest.responses.PartitionResponse; import org.apache.paimon.shade.guava30.com.google.common.collect.ImmutableList; import org.apache.paimon.shade.guava30.com.google.common.collect.Lists; @@ -147,6 +147,7 @@ public GetDatabaseResponse getDatabases( summary = "Drop Database", tags = {"database"}) @ApiResponses({ + @ApiResponse(responseCode = "200", description = "Success, no content"), @ApiResponse( responseCode = "404", description = "Resource not found", @@ -305,6 +306,7 @@ public GetTableResponse alterTable( summary = "Drop table", tags = {"table"}) @ApiResponses({ + @ApiResponse(responseCode = "200", description = "Success, no content"), @ApiResponse( responseCode = "404", description = "Resource not found", @@ -375,8 +377,7 @@ public ListPartitionsResponse listPartitions( @PathVariable String table) { Map spec = new HashMap<>(); spec.put("f1", "1"); - ListPartitionsResponse.Partition partition = - new ListPartitionsResponse.Partition(spec, 1, 2, 3, 4); + PartitionResponse partition = new PartitionResponse(spec, 1, 2, 3, 4); return new ListPartitionsResponse(ImmutableList.of(partition)); } @@ -384,9 +385,7 @@ public ListPartitionsResponse listPartitions( summary = "Create partition", tags = {"partition"}) @ApiResponses({ - @ApiResponse( - responseCode = "200", - content = {@Content(schema = @Schema(implementation = SuccessResponse.class))}), + @ApiResponse(responseCode = "200", description = "Success, no content"), @ApiResponse( responseCode = "404", description = "Resource not found", @@ -396,21 +395,20 @@ public ListPartitionsResponse listPartitions( content = {@Content(schema = @Schema())}) }) @PostMapping("/v1/{prefix}/databases/{database}/tables/{table}/partitions") - public SuccessResponse createPartition( + public PartitionResponse createPartition( @PathVariable String prefix, @PathVariable String database, @PathVariable String table, @RequestBody CreatePartitionRequest request) { - return new SuccessResponse(); + Map spec = new HashMap<>(); + spec.put("f1", "1"); + return new PartitionResponse(spec, 0, 0, 0, 4); } @Operation( summary = "Drop partition", tags = {"partition"}) @ApiResponses({ - @ApiResponse( - responseCode = "200", - content = {@Content(schema = @Schema(implementation = SuccessResponse.class))}), @ApiResponse( responseCode = "404", description = "Resource not found", @@ -420,11 +418,9 @@ public SuccessResponse createPartition( content = {@Content(schema = @Schema())}) }) @DeleteMapping("/v1/{prefix}/databases/{database}/tables/{table}/partitions") - public SuccessResponse dropPartition( + public void dropPartition( @PathVariable String prefix, @PathVariable String database, @PathVariable String table, - @RequestBody DropPartitionRequest request) { - return new SuccessResponse(); - } + @RequestBody DropPartitionRequest request) {} }