Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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
2 changes: 1 addition & 1 deletion cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -419,4 +419,4 @@ add_custom_target(graphar-clformat
add_custom_target(graphar-cpplint
COMMAND ${PROJECT_SOURCE_DIR}/misc/cpplint.py --root=${PROJECT_SOURCE_DIR}/include ${FILES_NEED_LINT}
COMMENT "Running cpplint check."
VERBATIM)
VERBATIM)
44 changes: 24 additions & 20 deletions cpp/src/graphar/graph_info.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "graphar/graph_info.h"
#include "graphar/result.h"
#include "graphar/types.h"
#include "graphar/util.h"
#include "graphar/version_parser.h"
#include "graphar/yaml.h"

Expand Down Expand Up @@ -1100,24 +1101,6 @@ Status EdgeInfo::Save(const std::string& path) const {

namespace {

static std::string PathToDirectory(const std::string& path) {
if (path.rfind("s3://", 0) == 0) {
size_t t = path.find_last_of('?');
std::string prefix = path.substr(0, t);
std::string suffix = path.substr(t);
const size_t last_slash_idx = prefix.rfind('/');
if (std::string::npos != last_slash_idx) {
return prefix.substr(0, last_slash_idx + 1) + suffix;
}
} else {
const size_t last_slash_idx = path.rfind('/');
if (std::string::npos != last_slash_idx) {
return path.substr(0, last_slash_idx + 1); // +1 to include the slash
}
}
return path;
}

static Result<std::shared_ptr<GraphInfo>> ConstructGraphInfo(
std::shared_ptr<Yaml> graph_meta, const std::string& default_name,
const std::string& default_prefix, const std::shared_ptr<FileSystem> fs,
Expand Down Expand Up @@ -1409,8 +1392,8 @@ Result<std::shared_ptr<GraphInfo>> GraphInfo::Load(const std::string& path) {
fs->ReadFileToValue<std::string>(no_url_path));
GAR_ASSIGN_OR_RAISE(auto graph_meta, Yaml::Load(yaml_content));
std::string default_name = "graph";
std::string default_prefix = PathToDirectory(path);
no_url_path = PathToDirectory(no_url_path);
std::string default_prefix = util::PathToDirectory(path);
no_url_path = util::PathToDirectory(no_url_path);
return ConstructGraphInfo(graph_meta, default_name, default_prefix, fs,
no_url_path);
}
Expand Down Expand Up @@ -1485,4 +1468,25 @@ Status GraphInfo::Save(const std::string& path) const {
return fs->WriteValueToFile(yaml_content, no_url_path);
}

namespace util {
std::string PathToDirectory(const std::string& path) {
if (path.rfind("s3://", 0) == 0) {
size_t t = path.find_last_of('?');
// Your fix here
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a leftover debug/placeholder comment that should be removed before merging. It doesn't describe the logic and reads like a TODO marker from development.

Copilot uses AI. Check for mistakes.
std::string prefix = (t == std::string::npos) ? path : path.substr(0, t);
std::string suffix = (t == std::string::npos) ? "" : path.substr(t);

const size_t last_slash_idx = prefix.rfind('/');
if (std::string::npos != last_slash_idx) {
return prefix.substr(0, last_slash_idx + 1) + suffix;
}
} else {
const size_t last_slash_idx = path.rfind('/');
if (std::string::npos != last_slash_idx) {
return path.substr(0, last_slash_idx + 1);
}
}
return path;
}
} // namespace util
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PathToDirectory function definition is placed in graph_info.cc, but it is declared in util.h and the natural implementation file for graphar::util functions is util.cc (which already exists at cpp/src/graphar/util.cc and contains the other graphar::util function definitions). Moving the definition to util.cc would be consistent with the existing codebase organization and avoid mixing unrelated concerns in graph_info.cc.

Copilot uses AI. Check for mistakes.
} // namespace graphar
2 changes: 2 additions & 0 deletions cpp/src/graphar/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -273,4 +273,6 @@ static arrow::Status OpenParquetArrowReader(
return arrow::Status::OK();
}

std::string PathToDirectory(const std::string& path);

} // namespace graphar::util
1 change: 1 addition & 0 deletions cpp/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,4 @@ add_graphar_test(test_arrow_chunk_reader SRCS test_arrow_chunk_reader.cc)
add_graphar_test(test_graph SRCS test_graph.cc)
add_graphar_test(test_multi_label SRCS test_multi_label.cc)
add_graphar_test(test_multi_property SRCS test_multi_property.cc)
add_graphar_test(test_util SRCS test_util.cc)
56 changes: 56 additions & 0 deletions cpp/test/test_util.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* 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.
*/

#include <iostream>
#include <string>

#include <catch2/catch_test_macros.hpp>
#include "./util.h"
#include "graphar/util.h"

namespace graphar {

TEST_CASE_METHOD(GlobalFixture, "PathUtilTest") {
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using GlobalFixture here means these pure string-manipulation tests will throw a std::runtime_error if the GAR_TEST_DATA environment variable is not set, even though these tests don't use any test data files at all. Consider using a plain TEST_CASE instead of TEST_CASE_METHOD(GlobalFixture, ...) so these unit tests can run without that environment dependency.

Copilot uses AI. Check for mistakes.
SECTION("PathToDirectory_S3_Handling") {
// 1. Test standard S3 URI with query (existing functionality)
std::string s3_with_query = "s3://bucket/path/graph.yml?version=123";
REQUIRE(util::PathToDirectory(s3_with_query) ==
"s3://bucket/path/?version=123");

// 2. Test S3 URI WITHOUT query (The Bug Fix!)
std::string s3_no_query = "s3://bucket/path/graph.yml";
REQUIRE(util::PathToDirectory(s3_no_query) == "s3://bucket/path/");

// 3. Test S3 URI at root
std::string s3_root = "s3://bucket/graph.yml";
REQUIRE(util::PathToDirectory(s3_root) == "s3://bucket/");
}

SECTION("PathToDirectory_Local_Handling") {
// 4. Test standard local path
std::string local_path = "/tmp/data/graph.yml";
REQUIRE(util::PathToDirectory(local_path) == "/tmp/data/");

// 5. Test relative path
std::string relative_path = "graph.yml";
REQUIRE(util::PathToDirectory(relative_path) == "graph.yml");
}
}

} // namespace graphar
Loading