Skip to content

Commit

Permalink
[#4374] feat(client): Add owner operations for client (#4421)
Browse files Browse the repository at this point in the history
### What changes were proposed in this pull request?

Add owner operations for client

### Why are the changes needed?

Fix: #4374

### Does this PR introduce _any_ user-facing change?
I will add the document later.

### How was this patch tested?
Add UTS.

Co-authored-by: roryqi <[email protected]>
  • Loading branch information
github-actions[bot] and jerqi authored Aug 8, 2024
1 parent 62aa8fb commit 6279630
Show file tree
Hide file tree
Showing 15 changed files with 447 additions and 85 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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.exceptions;

import com.google.errorprone.annotations.FormatMethod;
import com.google.errorprone.annotations.FormatString;

/** Exception thrown when a metadata object with specified name doesn't exist. */
public class NoSuchMetadataObjectException extends NotFoundException {
/**
* Constructs a new exception with the specified detail message.
*
* @param message the detail message.
* @param args the arguments to the message.
*/
@FormatMethod
public NoSuchMetadataObjectException(@FormatString String message, Object... args) {
super(message, args);
}

/**
* Constructs a new exception with the specified detail message and cause.
*
* @param cause the cause.
* @param message the detail message.
* @param args the arguments to the message.
*/
@FormatMethod
public NoSuchMetadataObjectException(Throwable cause, String message, Object... args) {
super(cause, message, args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.apache.gravitino.exceptions.NoSuchCatalogException;
import org.apache.gravitino.exceptions.NoSuchFilesetException;
import org.apache.gravitino.exceptions.NoSuchGroupException;
import org.apache.gravitino.exceptions.NoSuchMetadataObjectException;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;
import org.apache.gravitino.exceptions.NoSuchPartitionException;
import org.apache.gravitino.exceptions.NoSuchRoleException;
Expand Down Expand Up @@ -188,6 +189,15 @@ public static Consumer<ErrorResponse> tagErrorHandler() {
return TagErrorHandler.INSTANCE;
}

/**
* Creates an error handler specific to Owner operations.
*
* @return A Consumer representing the Owner error handler.
*/
public static Consumer<ErrorResponse> ownerErrorHandler() {
return OwnerErrorHandler.INSTANCE;
}

private ErrorHandlers() {}

/**
Expand Down Expand Up @@ -713,6 +723,36 @@ public void accept(ErrorResponse errorResponse) {
}
}

/** Error handler specific to Owner operations. */
@SuppressWarnings("FormatStringAnnotation")
private static class OwnerErrorHandler extends RestErrorHandler {

private static final OwnerErrorHandler INSTANCE = new OwnerErrorHandler();

@Override
public void accept(ErrorResponse errorResponse) {
String errorMessage = formatErrorMessage(errorResponse);

switch (errorResponse.getCode()) {
case ErrorConstants.ILLEGAL_ARGUMENTS_CODE:
throw new IllegalArgumentException(errorMessage);

case ErrorConstants.NOT_FOUND_CODE:
if (errorResponse.getType().equals(NoSuchMetadataObjectException.class.getSimpleName())) {
throw new NoSuchMetadataObjectException(errorMessage);
} else {
throw new NotFoundException(errorMessage);
}

case ErrorConstants.INTERNAL_ERROR_CODE:
throw new RuntimeException(errorMessage);

default:
super.accept(errorResponse);
}
}
}

/** Generic error handler for REST requests. */
private static class RestErrorHandler extends ErrorHandler {
private static final ErrorHandler INSTANCE = new RestErrorHandler();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,26 @@
import com.google.common.base.Preconditions;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.gravitino.Catalog;
import org.apache.gravitino.CatalogChange;
import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.SupportsCatalogs;
import org.apache.gravitino.authorization.Group;
import org.apache.gravitino.authorization.Owner;
import org.apache.gravitino.authorization.Role;
import org.apache.gravitino.authorization.SecurableObject;
import org.apache.gravitino.authorization.User;
import org.apache.gravitino.exceptions.CatalogAlreadyExistsException;
import org.apache.gravitino.exceptions.GroupAlreadyExistsException;
import org.apache.gravitino.exceptions.NoSuchCatalogException;
import org.apache.gravitino.exceptions.NoSuchGroupException;
import org.apache.gravitino.exceptions.NoSuchMetadataObjectException;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;
import org.apache.gravitino.exceptions.NoSuchRoleException;
import org.apache.gravitino.exceptions.NoSuchTagException;
import org.apache.gravitino.exceptions.NoSuchUserException;
import org.apache.gravitino.exceptions.NotFoundException;
import org.apache.gravitino.exceptions.RoleAlreadyExistsException;
import org.apache.gravitino.exceptions.TagAlreadyExistsException;
import org.apache.gravitino.exceptions.UserAlreadyExistsException;
Expand Down Expand Up @@ -308,6 +313,31 @@ public Group revokeRolesFromGroup(List<String> roles, String group)
return getMetalake().revokeRolesFromGroup(roles, group);
}

/**
* Get the owner of a metadata object.
*
* @param object The metadata object
* @return The owner of the metadata object. If the metadata object doesn't set the owner, it will
* return Optional.empty().
* @throws NoSuchMetadataObjectException If the metadata object is not found.
*/
public Optional<Owner> getOwner(MetadataObject object) throws NoSuchMetadataObjectException {
return getMetalake().getOwner(object);
}

/**
* Set the owner of a metadata object.
*
* @param object The metadata object.
* @param ownerName The name of the owner
* @param ownerType The type of the owner, The owner can be a user or a group.
* @throws NotFoundException If the metadata object isn't found or the owner doesn't exist.
*/
public void setOwner(MetadataObject object, String ownerName, Owner.Type ownerType)
throws NotFoundException {
getMetalake().setOwner(object, ownerName, ownerType);
}

/**
* Creates a new builder for constructing a GravitinoClient.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,18 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.gravitino.Catalog;
import org.apache.gravitino.CatalogChange;
import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.SupportsCatalogs;
import org.apache.gravitino.authorization.Group;
import org.apache.gravitino.authorization.Owner;
import org.apache.gravitino.authorization.Role;
import org.apache.gravitino.authorization.SecurableObject;
import org.apache.gravitino.authorization.User;
Expand All @@ -42,6 +46,7 @@
import org.apache.gravitino.dto.requests.CatalogUpdateRequest;
import org.apache.gravitino.dto.requests.CatalogUpdatesRequest;
import org.apache.gravitino.dto.requests.GroupAddRequest;
import org.apache.gravitino.dto.requests.OwnerSetRequest;
import org.apache.gravitino.dto.requests.RoleCreateRequest;
import org.apache.gravitino.dto.requests.RoleGrantRequest;
import org.apache.gravitino.dto.requests.RoleRevokeRequest;
Expand All @@ -57,19 +62,23 @@
import org.apache.gravitino.dto.responses.ErrorResponse;
import org.apache.gravitino.dto.responses.GroupResponse;
import org.apache.gravitino.dto.responses.NameListResponse;
import org.apache.gravitino.dto.responses.OwnerResponse;
import org.apache.gravitino.dto.responses.RemoveResponse;
import org.apache.gravitino.dto.responses.RoleResponse;
import org.apache.gravitino.dto.responses.SetResponse;
import org.apache.gravitino.dto.responses.TagListResponse;
import org.apache.gravitino.dto.responses.TagResponse;
import org.apache.gravitino.dto.responses.UserResponse;
import org.apache.gravitino.exceptions.CatalogAlreadyExistsException;
import org.apache.gravitino.exceptions.GroupAlreadyExistsException;
import org.apache.gravitino.exceptions.NoSuchCatalogException;
import org.apache.gravitino.exceptions.NoSuchGroupException;
import org.apache.gravitino.exceptions.NoSuchMetadataObjectException;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;
import org.apache.gravitino.exceptions.NoSuchRoleException;
import org.apache.gravitino.exceptions.NoSuchTagException;
import org.apache.gravitino.exceptions.NoSuchUserException;
import org.apache.gravitino.exceptions.NotFoundException;
import org.apache.gravitino.exceptions.RoleAlreadyExistsException;
import org.apache.gravitino.exceptions.TagAlreadyExistsException;
import org.apache.gravitino.exceptions.UserAlreadyExistsException;
Expand All @@ -88,6 +97,7 @@ public class GravitinoMetalake extends MetalakeDTO implements SupportsCatalogs,
private static final String API_METALAKES_USERS_PATH = "api/metalakes/%s/users/%s";
private static final String API_METALAKES_GROUPS_PATH = "api/metalakes/%s/groups/%s";
private static final String API_METALAKES_ROLES_PATH = "api/metalakes/%s/roles/%s";
private static final String API_METALAKES_OWNERS_PATH = "api/metalakes/%s/owners/%s";
private static final String BLANK_PLACE_HOLDER = "";

private static final String API_METALAKES_TAGS_PATH = "api/metalakes/%s/tags";
Expand Down Expand Up @@ -762,6 +772,55 @@ public Group revokeRolesFromGroup(List<String> roles, String group)
return resp.getGroup();
}

/**
* Get the owner of a metadata object.
*
* @param object The metadata object
* @return The owner of the metadata object. If the metadata object doesn't set the owner, it will
* return Optional.empty().
* @throws NoSuchMetadataObjectException If the metadata object is not found.
*/
public Optional<Owner> getOwner(MetadataObject object) throws NoSuchMetadataObjectException {
OwnerResponse resp =
restClient.get(
String.format(
API_METALAKES_OWNERS_PATH,
this.name(),
String.format(
"%s/%s", object.type().name().toLowerCase(Locale.ROOT), object.fullName())),
OwnerResponse.class,
Collections.emptyMap(),
ErrorHandlers.ownerErrorHandler());
resp.validate();
return Optional.ofNullable(resp.getOwner());
}

/**
* Set the owner of a metadata object.
*
* @param object The metadata object.
* @param ownerName The name of the owner
* @param ownerType The type of the owner, The owner can be a user or a group.
* @throws NotFoundException If the metadata object isn't found or the owner doesn't exist.
*/
public void setOwner(MetadataObject object, String ownerName, Owner.Type ownerType)
throws NotFoundException {
OwnerSetRequest request = new OwnerSetRequest(ownerName, ownerType);
request.validate();
SetResponse resp =
restClient.put(
String.format(
API_METALAKES_OWNERS_PATH,
this.name(),
String.format(
"%s/%s", object.type().name().toLowerCase(Locale.ROOT), object.fullName())),
request,
SetResponse.class,
Collections.emptyMap(),
ErrorHandlers.ownerErrorHandler());
resp.validate();
}

static class Builder extends MetalakeDTO.Builder<Builder> {
private RESTClient restClient;

Expand Down
Loading

0 comments on commit 6279630

Please sign in to comment.