Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ public class API {
MetricsUtil.registerMeter(API.class, "expected-error");
private static final Meter unknownErrorMeter =
MetricsUtil.registerMeter(API.class, "unknown-error");
private static final String STANDALONE_ERROR =
"GraphSpace management is not supported in standalone mode";

public static HugeGraph graph(GraphManager manager, String graphSpace,
String graph) {
Expand Down Expand Up @@ -241,6 +243,20 @@ public static boolean checkAndParseAction(String action) {
}
}

/**
* Ensures the graph manager is available and PD mode is enabled.
*
* @param manager the graph manager of current request
* @throws IllegalArgumentException if the graph manager is null
* @throws HugeException if PD mode is disabled
*/
protected static void ensurePdModeEnabled(GraphManager manager) {
E.checkArgumentNotNull(manager, "Graph manager can't be null");
if (!manager.isPDEnable()) {
throw new HugeException(STANDALONE_ERROR);
}
}

public static boolean hasAdminPerm(GraphManager manager, String user) {
return manager.authManager().isAdminManager(user);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public String createManager(@Context GraphManager manager,
@PathParam("graphspace") String graphSpace,
JsonManager jsonManager) {
LOG.debug("Create manager: {}", jsonManager);
ensurePdModeEnabled(manager);
String user = jsonManager.user;
HugePermission type = jsonManager.type;
// graphSpace now comes from @PathParam instead of JsonManager
Expand Down Expand Up @@ -117,6 +118,7 @@ public void delete(@Context GraphManager manager,
@QueryParam("user") String user,
@QueryParam("type") HugePermission type) {
LOG.debug("Delete graph manager: {} {} {}", user, type, graphSpace);
ensurePdModeEnabled(manager);
E.checkArgument(!"admin".equals(user) ||
type != HugePermission.ADMIN,
"User 'admin' can't be removed from ADMIN");
Expand Down Expand Up @@ -160,7 +162,7 @@ public String list(@Context GraphManager manager,
@PathParam("graphspace") String graphSpace,
@QueryParam("type") HugePermission type) {
LOG.debug("list graph manager: {} {}", type, graphSpace);

ensurePdModeEnabled(manager);
AuthManager authManager = manager.authManager();
validType(type);
List<String> adminManagers;
Expand Down Expand Up @@ -190,7 +192,7 @@ public String checkRole(@Context GraphManager manager,
@PathParam("graphspace") String graphSpace,
@QueryParam("type") HugePermission type) {
LOG.debug("check if current user is graph manager: {} {}", type, graphSpace);

ensurePdModeEnabled(manager);
validType(type);
AuthManager authManager = manager.authManager();
String user = HugeGraphAuthProxy.username();
Expand Down Expand Up @@ -222,6 +224,7 @@ public String getRolesInGs(@Context GraphManager manager,
@PathParam("graphspace") String graphSpace,
@QueryParam("user") String user) {
LOG.debug("get user [{}]'s role in graph space [{}]", user, graphSpace);
ensurePdModeEnabled(manager);
AuthManager authManager = manager.authManager();
List<HugePermission> result = new ArrayList<>();
validGraphSpace(manager, graphSpace);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ public class GraphSpaceAPI extends API {
@Produces(APPLICATION_JSON_WITH_CHARSET)
public Object list(@Context GraphManager manager,
@Context SecurityContext sc) {
ensurePdModeEnabled(manager);
Set<String> spaces = manager.graphSpaces();
return ImmutableMap.of("graphSpaces", spaces);
}
Expand All @@ -86,6 +87,7 @@ public Object list(@Context GraphManager manager,
@Produces(APPLICATION_JSON_WITH_CHARSET)
public Object get(@Context GraphManager manager,
@PathParam("graphspace") String graphSpace) {
ensurePdModeEnabled(manager);
manager.getSpaceStorage(graphSpace);
GraphSpace gs = space(manager, graphSpace);

Expand All @@ -106,6 +108,7 @@ public Object get(@Context GraphManager manager,
public Object listProfile(@Context GraphManager manager,
@QueryParam("prefix") String prefix,
@Context SecurityContext sc) {
ensurePdModeEnabled(manager);
Set<String> spaces = manager.graphSpaces();
List<Map<String, Object>> spaceList = new ArrayList<>();
List<Map<String, Object>> result = new ArrayList<>();
Expand Down Expand Up @@ -155,7 +158,7 @@ public Object listProfile(@Context GraphManager manager,
@RolesAllowed({"admin"})
public String create(@Context GraphManager manager,
JsonGraphSpace jsonGraphSpace) {

ensurePdModeEnabled(manager);
jsonGraphSpace.checkCreate(false);

String creator = HugeGraphAuthProxy.username();
Expand Down Expand Up @@ -186,7 +189,7 @@ public boolean isPrefix(Map<String, Object> profile, String prefix) {
public Map<String, Object> manage(@Context GraphManager manager,
@PathParam("name") String name,
Map<String, Object> actionMap) {

ensurePdModeEnabled(manager);
E.checkArgument(actionMap != null && actionMap.size() == 2 &&
actionMap.containsKey(GRAPH_SPACE_ACTION),
"Invalid request body '%s'", actionMap);
Expand Down Expand Up @@ -315,6 +318,7 @@ public Map<String, Object> manage(@Context GraphManager manager,
@RolesAllowed({"admin"})
public void delete(@Context GraphManager manager,
@PathParam("name") String name) {
ensurePdModeEnabled(manager);
manager.dropGraphSpace(name);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ private static String serviceId(String graphSpace, Service.ServiceType type,
.replace("_", "-").toLowerCase();
}

private boolean usePD() {
public boolean isPDEnable() {
return this.PDExist;
}

Comment on lines +279 to 282
Expand Down Expand Up @@ -1227,7 +1227,7 @@ private void dropGraphLocal(HugeGraph graph) {

public HugeGraph createGraph(String graphSpace, String name, String creator,
Map<String, Object> configs, boolean init) {
if (!usePD()) {
if (!isPDEnable()) {
// Extract nickname from configs
String nickname;
if (configs.get("nickname") != null) {
Expand Down Expand Up @@ -1937,7 +1937,7 @@ public Set<String> getServiceUrls(String graphSpace, String service,
public HugeGraph graph(String graphSpace, String name) {
String key = String.join(DELIMITER, graphSpace, name);
Graph graph = this.graphs.get(key);
if (graph == null && usePD()) {
if (graph == null && isPDEnable()) {
Map<String, Map<String, Object>> configs =
this.metaManager.graphConfigs(graphSpace);
// If current server registered graph space is not DEFAULT, only load graph creation
Expand Down Expand Up @@ -1981,7 +1981,7 @@ public void dropGraphLocal(String name) {
}

public void dropGraph(String graphSpace, String name, boolean clear) {
if (!usePD()) {
if (!isPDEnable()) {
dropGraphLocal(name);
return;
}
Expand Down Expand Up @@ -2086,7 +2086,7 @@ private void checkOptionsUnique(String graphSpace,
public Set<String> graphs(String graphSpace) {
Set<String> graphs = new HashSet<>();

if (!usePD()) {
if (!isPDEnable()) {
for (String key : this.graphs.keySet()) {
String[] parts = key.split(DELIMITER);
if (parts[0].equals(graphSpace)) {
Expand All @@ -2103,7 +2103,7 @@ public Set<String> graphs(String graphSpace) {
}

public GraphSpace graphSpace(String name) {
if (!usePD()) {
if (!isPDEnable()) {
return new GraphSpace("DEFAULT");
}
GraphSpace space = this.graphSpaces.get(name);
Expand Down Expand Up @@ -2152,7 +2152,7 @@ private MapConfiguration buildConfig(Map<String, Object> configs) {
public void graphReadMode(String graphSpace, String graphName,
GraphReadMode readMode) {

if (!usePD()) {
if (!isPDEnable()) {
HugeGraph g = this.graph(spaceGraphName(graphSpace, graphName));
g.readMode(readMode);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
CypherApiTest.class,
ArthasApiTest.class,
GraphSpaceApiTest.class,
GraphSpaceApiStandaloneTest.class,
ManagerApiStandaloneTest.class,
})
public class ApiTestSuite {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.BeforeClass;

import com.fasterxml.jackson.databind.JavaType;
Expand All @@ -61,9 +62,9 @@ public class BaseApiTest {
protected static final String BASE_URL = "http://127.0.0.1:8080";
private static final String GRAPH = "hugegraph";
private static final String GRAPHSPACE = "DEFAULT";
private static final String USERNAME = "admin";
protected static final String URL_PREFIX = "graphspaces/" + GRAPHSPACE + "/graphs/" + GRAPH;
protected static final String TRAVERSERS_API = URL_PREFIX + "/traversers";
private static final String USERNAME = "admin";
private static final String PASSWORD = "pa";
private static final int NO_LIMIT = -1;
private static final String SCHEMA_PKS = "/schema/propertykeys";
Expand All @@ -82,10 +83,8 @@ public class BaseApiTest {
"\"rocksdb.wal_path\": \"rocksdbtest-data-%s\"," +
"\"search.text_analyzer\": \"jieba\"," +
"\"search.text_analyzer_mode\": \"INDEX\" }";

protected static RestClient client;

private static final ObjectMapper MAPPER = new ObjectMapper();
protected static RestClient client;

@BeforeClass
public static void init() {
Expand All @@ -99,19 +98,10 @@ public static void clear() throws Exception {
client = null;
}

@After
public void teardown() throws Exception {
BaseApiTest.clearData();
}

public static String baseUrl() {
return BASE_URL;
}

public RestClient client() {
return client;
}

public static RestClient newClient() {
return new RestClient(BASE_URL);
}
Expand Down Expand Up @@ -193,7 +183,8 @@ protected static void waitTaskStatus(int task, Set<String> expectedStatus) {
Assert.fail(String.format("Failed to wait for task %s " +
"due to timeout", task));
}
} while (!expectedStatus.contains(status));
}
while (!expectedStatus.contains(status));
}

protected static void initVertexLabel() {
Expand Down Expand Up @@ -748,6 +739,27 @@ public static RestClient analystClient(String graphSpace, String username) {
return analystClient;
}

/**
* Skips the current test if the server is running in hstore / PD mode.
* Treats both {@code "hstore"} and {@code null} (i.e. the property is not
* set, which is the default in hstore CI runs) as PD mode.
* Call this from a {@code @Before} method in standalone-only test classes.
*/
public static void assumeStandaloneMode() {
String backend = System.getProperty("backend");
boolean isPdMode = backend == null || "hstore".equals(backend);
Assume.assumeFalse("Skip in PD/distributed mode", isPdMode);
}

@After
public void teardown() throws Exception {
BaseApiTest.clearData();
}

public RestClient client() {
return client;
}

public static class RestClient {

private final Client client;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* 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.hugegraph.api;

import com.google.common.collect.ImmutableMap;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import jakarta.ws.rs.core.Response;

/**
* Tests that GraphSpaceAPI returns a friendly HTTP 400 error in standalone
* mode (i.e. when the server is started without PD / hstore backend).
* <p>
* This class intentionally does NOT have a class-level Assume guard so that
* the tests are actually executed in non-hstore CI runs.
*/
public class GraphSpaceApiStandaloneTest extends BaseApiTest {

private static final String PATH = "graphspaces";
private static final String STANDALONE_ERROR =
"GraphSpace management is not supported in standalone mode";

@Before
public void skipForPdMode() {
assumeStandaloneMode();
}

@Test
public void testProfileReturnsFriendlyError() {
Response r = this.client().get(PATH + "/profile");
String content = assertResponseStatus(400, r);
Assert.assertTrue(content.contains(STANDALONE_ERROR));
}

@Test
public void testListReturnsFriendlyError() {
Response r = this.client().get(PATH);
String content = assertResponseStatus(400, r);
Assert.assertTrue(content.contains(STANDALONE_ERROR));
}

@Test
public void testGetReturnsFriendlyError() {
Response r = this.client().get(PATH, "DEFAULT");
String content = assertResponseStatus(400, r);
Assert.assertTrue(content.contains(STANDALONE_ERROR));
}

@Test
public void testCreateReturnsFriendlyError() {
String body = "{\"name\":\"test_standalone\",\"nickname\":\"test\","
+ "\"description\":\"test\",\"cpu_limit\":10,"
+ "\"memory_limit\":10,\"storage_limit\":10,"
+ "\"max_graph_number\":10,\"max_role_number\":10,"
+ "\"auth\":false,\"configs\":{}}";
Response r = this.client().post(PATH, body);
String content = assertResponseStatus(400, r);
Assert.assertTrue(content.contains(STANDALONE_ERROR));
}

@Test
public void testManageReturnsFriendlyError() {
String body = "{\"action\":\"update\",\"update\":{\"name\":\"DEFAULT\"}}";
Response r = this.client().put(PATH, "DEFAULT", body, ImmutableMap.of());
String content = assertResponseStatus(400, r);
Assert.assertTrue(content.contains(STANDALONE_ERROR));
}

@Test
public void testDeleteReturnsFriendlyError() {
Response r = this.client().delete(PATH, "nonexistent");
String content = assertResponseStatus(400, r);
Assert.assertTrue(content.contains(STANDALONE_ERROR));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public void removeSpaces() {
Response r = this.client().get(PATH);
String result = r.readEntity(String.class);
Map<String, Object> resultMap = JsonUtil.fromJson(result, Map.class);
List<String> spaces = (List<String>) resultMap.get("graphSpaces");
List<String> spaces = (List<String>)resultMap.get("graphSpaces");
for (String space : spaces) {
if (!"DEFAULT".equals(space)) {
this.client().delete(PATH, space);
Expand Down
Loading
Loading