Skip to content

Commit

Permalink
use PATCH method for REST API
Browse files Browse the repository at this point in the history
  • Loading branch information
mchades committed Oct 17, 2024
1 parent 7fefe99 commit fab7bef
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.apache.gravitino.dto.MetalakeDTO;
import org.apache.gravitino.dto.authorization.SecurableObjectDTO;
import org.apache.gravitino.dto.requests.CatalogCreateRequest;
import org.apache.gravitino.dto.requests.CatalogSetRequest;
import org.apache.gravitino.dto.requests.CatalogUpdateRequest;
import org.apache.gravitino.dto.requests.CatalogUpdatesRequest;
import org.apache.gravitino.dto.requests.GroupAddRequest;
Expand Down Expand Up @@ -297,9 +298,12 @@ public boolean dropCatalog(String catalogName, boolean force)

@Override
public void enableCatalog(String catalogName) throws NoSuchCatalogException {
CatalogSetRequest req = new CatalogSetRequest(true);

ErrorResponse resp =
restClient.get(
String.format(API_METALAKES_CATALOGS_PATH, this.name(), catalogName) + "/activate",
restClient.patch(
String.format(API_METALAKES_CATALOGS_PATH, this.name(), catalogName),
req,
ErrorResponse.class,
Collections.emptyMap(),
ErrorHandlers.catalogErrorHandler());
Expand All @@ -313,9 +317,12 @@ public void enableCatalog(String catalogName) throws NoSuchCatalogException {

@Override
public void disableCatalog(String catalogName) throws NoSuchCatalogException {
CatalogSetRequest req = new CatalogSetRequest(false);

ErrorResponse resp =
restClient.get(
String.format(API_METALAKES_CATALOGS_PATH, this.name(), catalogName) + "/disable",
restClient.patch(
String.format(API_METALAKES_CATALOGS_PATH, this.name(), catalogName),
req,
ErrorResponse.class,
Collections.emptyMap(),
ErrorHandlers.catalogErrorHandler());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,31 @@ public <T extends RESTResponse> T put(
Method.PUT, path, null, body, responseType, headers, errorHandler, responseHeaders);
}

/**
* Sends an HTTP PATCH request to the specified path with the provided request body and processes
* the response with support for response headers.
*
* @param path The URL path to send the PATCH request to.
* @param body The REST request to place in the request body.
* @param responseType The class type of the response for deserialization (Must be registered with
* the ObjectMapper).
* @param headers A map of request headers (key-value pairs) to include in the request (can be
* null).
* @param errorHandler The error handler delegated for HTTP responses, which handles server error
* responses.
* @return The response entity parsed and converted to its type T.
* @param <T> The class type of the response for deserialization.
*/
@Override
public <T extends RESTResponse> T patch(
String path,
RESTRequest body,
Class<T> responseType,
Map<String, String> headers,
Consumer<ErrorResponse> errorHandler) {
return execute(Method.PATCH, path, null, body, responseType, headers, errorHandler);
}

/**
* Sends an HTTP DELETE request to the specified path without query parameters and processes the
* response.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,24 @@ <T extends RESTResponse> T put(
Map<String, String> headers,
Consumer<ErrorResponse> errorHandler);

/**
* Perform a PATCH request on the specified path with given information.
*
* @param path The path to be requested.
* @param body The request body to be included in the PATCH request.
* @param responseType The class representing the type of the response.
* @param headers The headers to be included in the request.
* @param errorHandler The consumer for handling error responses.
* @return The response of the PATCH request.
* @param <T> The type of the response.
*/
<T extends RESTResponse> T patch(
String path,
RESTRequest body,
Class<T> responseType,
Map<String, String> headers,
Consumer<ErrorResponse> errorHandler);

/**
* Perform a POST request with form data on the specified path with the given information.
*
Expand Down
21 changes: 17 additions & 4 deletions clients/client-python/gravitino/client/gravitino_metalake.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from gravitino.dto.dto_converters import DTOConverters
from gravitino.dto.metalake_dto import MetalakeDTO
from gravitino.dto.requests.catalog_create_request import CatalogCreateRequest
from gravitino.dto.requests.catalog_set_request import CatalogSetRequest
from gravitino.dto.requests.catalog_updates_request import CatalogUpdatesRequest
from gravitino.dto.responses.catalog_list_response import CatalogListResponse
from gravitino.dto.responses.catalog_response import CatalogResponse
Expand Down Expand Up @@ -215,8 +216,14 @@ def enable_catalog(self, name: str):
Raises:
NoSuchCatalogException if the catalog with specified name does not exist.
"""
url = self.API_METALAKES_CATALOGS_PATH.format(self.name(), name) + "/enable"
self.rest_client.get(url, error_handler=CATALOG_ERROR_HANDLER)

catalog_enable_request = CatalogSetRequest(in_use=True)
catalog_enable_request.validate()

url = self.API_METALAKES_CATALOGS_PATH.format(self.name(), name)
self.rest_client.patch(
url, json=catalog_enable_request, error_handler=CATALOG_ERROR_HANDLER
)

def disable_catalog(self, name: str):
"""Disable the catalog with specified name. If the catalog is already disabled, this method does nothing.
Expand All @@ -227,5 +234,11 @@ def disable_catalog(self, name: str):
Raises:
NoSuchCatalogException if the catalog with specified name does not exist.
"""
url = self.API_METALAKES_CATALOGS_PATH.format(self.name(), name) + "/disable"
self.rest_client.get(url, error_handler=CATALOG_ERROR_HANDLER)

catalog_disable_request = CatalogSetRequest(in_use=False)
catalog_disable_request.validate()

url = self.API_METALAKES_CATALOGS_PATH.format(self.name(), name)
self.rest_client.patch(
url, json=catalog_disable_request, error_handler=CATALOG_ERROR_HANDLER
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# 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.

from dataclasses import dataclass, field

from dataclasses_json import config

from gravitino.rest.rest_message import RESTRequest


@dataclass
class CatalogSetRequest(RESTRequest):
"""Represents a request to set a catalog in use."""

_in_use: bool = field(metadata=config(field_name="inUse"))

def __init__(self, in_use: bool):
self._in_use = in_use

def validate(self):
"""Validates the fields of the request.
Raises:
IllegalArgumentException if in_use is not set.
"""
if self._in_use is None:
raise ValueError('"in_use" field is required and cannot be empty')
5 changes: 5 additions & 0 deletions clients/client-python/gravitino/utils/http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,11 @@ def put(self, endpoint, json=None, error_handler=None, **kwargs):
"put", endpoint, json=json, error_handler=error_handler, **kwargs
)

def patch(self, endpoint, json=None, error_handler=None, **kwargs):
return self._request(
"patch", endpoint, json=json, error_handler=error_handler, **kwargs
)

def post_form(self, endpoint, data=None, error_handler=None, **kwargs):
return self._request(
"post", endpoint, data=data, error_handler=error_handler, **kwargs
Expand Down
5 changes: 2 additions & 3 deletions clients/client-python/tests/integration/test_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,8 @@ def test_alter_catalog(self):

def test_drop_catalog(self):
self.create_catalog(self.catalog_name)
self.assertTrue(
self.gravitino_client.drop_catalog(name=self.catalog_name, force=True)
)
self.gravitino_client.disable_catalog(self.catalog_name)
self.assertTrue(self.gravitino_client.drop_catalog(name=self.catalog_name))

def test_list_catalogs_info(self):
self.create_catalog(self.catalog_name)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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.gravitino.dto.requests;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.apache.gravitino.rest.RESTRequest;

/** Represents a request to set a catalog in use. */
@Getter
@EqualsAndHashCode
@ToString
public class CatalogSetRequest implements RESTRequest {

@JsonProperty("inUse")
private final boolean inUse;

/** Default constructor for CatalogSetRequest. */
public CatalogSetRequest() {
this(false);
}

/**
* Constructor for CatalogSetRequest.
*
* @param inUse The in use status to set.
*/
public CatalogSetRequest(boolean inUse) {
this.inUse = inUse;
}

/**
* Validates the request. No validation needed.
*
* @throws IllegalArgumentException If the request is invalid.
*/
@Override
public void validate() throws IllegalArgumentException {
// No validation needed
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.PATCH;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
Expand All @@ -41,6 +42,7 @@
import org.apache.gravitino.Namespace;
import org.apache.gravitino.catalog.CatalogDispatcher;
import org.apache.gravitino.dto.requests.CatalogCreateRequest;
import org.apache.gravitino.dto.requests.CatalogSetRequest;
import org.apache.gravitino.dto.requests.CatalogUpdateRequest;
import org.apache.gravitino.dto.requests.CatalogUpdatesRequest;
import org.apache.gravitino.dto.responses.BaseResponse;
Expand Down Expand Up @@ -187,14 +189,16 @@ public Response testConnection(
}
}

@GET
@Path("{catalog}/enable")
@PATCH
@Path("{catalog}")
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "enable-catalog." + MetricNames.HTTP_PROCESS_DURATION, absolute = true)
@ResponseMetered(name = "enable-catalog", absolute = true)
public Response enableCatalog(
@PathParam("metalake") String metalake, @PathParam("catalog") String catalogName) {
LOG.info("Received enable request for catalog: {}.{}", metalake, catalogName);
@Timed(name = "set-catalog." + MetricNames.HTTP_PROCESS_DURATION, absolute = true)
@ResponseMetered(name = "set-catalog", absolute = true)
public Response setCatalog(
@PathParam("metalake") String metalake,
@PathParam("catalog") String catalogName,
CatalogSetRequest request) {
LOG.info("Received set request for catalog: {}.{}", metalake, catalogName);
try {
return Utils.doAs(
httpRequest,
Expand All @@ -204,53 +208,33 @@ public Response enableCatalog(
NameIdentifierUtil.ofMetalake(metalake),
LockType.WRITE,
() -> {
catalogDispatcher.enableCatalog(ident);
if (request.isInUse()) {
catalogDispatcher.enableCatalog(ident);
} else {
catalogDispatcher.disableCatalog(ident);
}
return null;
});
Response response = Utils.ok(new BaseResponse());
LOG.info("Successfully enable catalog: {}.{}", metalake, catalogName);
LOG.info(
"Successfully {} catalog: {}.{}",
request.isInUse() ? "enable" : "disable",
metalake,
catalogName);
return response;
});

} catch (Exception e) {
LOG.info("Failed to enable catalog: {}.{}", metalake, catalogName);
LOG.info(
"Failed to {} catalog: {}.{}",
request.isInUse() ? "enable" : "disable",
metalake,
catalogName);
return ExceptionHandlers.handleCatalogException(
OperationType.ENABLE, catalogName, metalake, e);
}
}

@GET
@Path("{catalog}/disable")
@Produces("disable/vnd.gravitino.v1+json")
@Timed(name = "disable-catalog." + MetricNames.HTTP_PROCESS_DURATION, absolute = true)
@ResponseMetered(name = "disable-catalog", absolute = true)
public Response disableCatalog(
@PathParam("metalake") String metalake, @PathParam("catalog") String catalogName) {
LOG.info("Received disable request for catalog: {}.{}", metalake, catalogName);
try {
return Utils.doAs(
httpRequest,
() -> {
NameIdentifier ident = NameIdentifierUtil.ofCatalog(metalake, catalogName);
TreeLockUtils.doWithTreeLock(
NameIdentifierUtil.ofMetalake(metalake),
LockType.WRITE,
() -> {
catalogDispatcher.disableCatalog(ident);
return null;
});
Response response = Utils.ok(new BaseResponse());
LOG.info("Successfully disable catalog: {}.{}", metalake, catalogName);
return response;
});

} catch (Exception e) {
LOG.info("Failed to disable catalog: {}.{}", metalake, catalogName);
return ExceptionHandlers.handleCatalogException(
OperationType.DISABLE, catalogName, metalake, e);
}
}

@GET
@Path("{catalog}")
@Produces("application/vnd.gravitino.v1+json")
Expand Down
Loading

0 comments on commit fab7bef

Please sign in to comment.