Skip to content

Commit

Permalink
Merge pull request #418 from gazebosim/forwardport/8-to-9-20240429
Browse files Browse the repository at this point in the history
Forwardport 8 to 9 2024-04-29
  • Loading branch information
ahcorde authored May 15, 2024
2 parents 3dbc344 + 52c86bc commit e808b0a
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 31 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,25 @@ Downloading model:
Download succeeded.
```

**Private access tokens**
Private models and worlds can be downloaded using access tokens.
Access tokens are generated on `app.gazebosim.org`. After logging in,
go to `Settings->Access Tokens`.

An access token can be used with CLI commands via the `--header` option:

```bash
$ gz fuel download -u https://fuel.gazebosim.org/1.0/openrobotics/models/ambulance --header 'Private-Token: <access_token>'
```

Or, an access token can be stored in a `~/.gz/fuel/config.yaml` file. The token is then
automatically used by the command line tool and API calls. Use the `configure` helper
tool create your `~/.gz/fuel/config.yaml` file.

```bash
$ gz fuel configure
```

**C++ Get List models**
```cpp
// Create a client (uses https://fuel.gazebosim.org by default)
Expand Down
4 changes: 0 additions & 4 deletions conf/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,3 @@ configure_file(

# Install the yaml configuration files in an unversioned location.
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fuel${PROJECT_VERSION_MAJOR}.yaml DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gz/)

# Install config.yaml
install (FILES config.yaml DESTINATION
${CMAKE_INSTALL_DATAROOTDIR}/gz/${GZ_DESIGNATION}${PROJECT_VERSION_MAJOR}/)
14 changes: 0 additions & 14 deletions conf/config.yaml

This file was deleted.

10 changes: 10 additions & 0 deletions include/gz/fuel_tools/WorldIdentifier.hh
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,16 @@ namespace gz::fuel_tools
/// \return World information string
public: std::string AsPrettyString(const std::string &_prefix = "") const;

/// \brief Returns the privacy setting of the world.
/// \return True if the world is private, false if the world is
/// public.
public: bool Private() const;

/// \brief Set the privacy setting of the world.
/// \param[in] _private True indicates the world is private,
/// false indicates the world is public.
public: void SetPrivate(bool _private);

/// \brief PIMPL
private: std::unique_ptr<WorldIdentifierPrivate> dataPtr;
};
Expand Down
22 changes: 14 additions & 8 deletions src/ClientConfig.cc
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,24 @@ ClientConfig::ClientConfig() : dataPtr(new ClientConfigPrivate)
<< "to set cache path. Please use [GZ_FUEL_CACHE_PATH] instead."
<< std::endl;
}
else
{
return;
}
}

if (!gz::common::isDirectory(gzFuelPath))
if (!gzFuelPath.empty())
{
gzerr << "[" << gzFuelPath << "] is not a directory" << std::endl;
return;
if (!gz::common::isDirectory(gzFuelPath))
gzerr << "[" << gzFuelPath << "] is not a directory" << std::endl;
else
this->SetCacheLocation(gzFuelPath);
}
this->SetCacheLocation(gzFuelPath);

std::string configYamlFile = common::joinPaths(this->CacheLocation(),
"config.yaml");
std::string configYmlFile = common::joinPaths(this->CacheLocation(),
"config.yml");
if (gz::common::exists(configYamlFile))
this->LoadConfig(configYamlFile);
else if (gz::common::exists(configYmlFile))
this->LoadConfig(configYmlFile);
}

//////////////////////////////////////////////////
Expand Down
17 changes: 16 additions & 1 deletion src/WorldIdentifier.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,12 @@ class WorldIdentifierPrivate
/// \brief World version. Valid versions start from 1, 0 means the tip.
public: unsigned int version{0};

/// \brief Path of this model in the local cache
/// \brief Path of this world in the local cache
public: std::string localPath;

/// \brief True indicates the world is private, false indicates the
/// world is public.
public: bool privacy{false};
};

//////////////////////////////////////////////////
Expand Down Expand Up @@ -229,4 +233,15 @@ std::string WorldIdentifier::AsPrettyString(const std::string &_prefix) const
<< this->Server().AsPrettyString(_prefix + " ");
return out.str();
}
//////////////////////////////////////////////////
bool WorldIdentifier::Private() const
{
return this->dataPtr->privacy;
}

//////////////////////////////////////////////////
void WorldIdentifier::SetPrivate(bool _private)
{
this->dataPtr->privacy = _private;
}
} // namespace gz::fuel_tools
6 changes: 6 additions & 0 deletions src/WorldIdentifier_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ TEST(WorldIdentifier, SetFields)
EXPECT_EQ(std::string("hello"), id.Name());
EXPECT_EQ(std::string("acai"), id.Owner());
EXPECT_EQ(6u, id.Version());

EXPECT_FALSE(id.Private());
id.SetPrivate(true);
EXPECT_TRUE(id.Private());
id.SetPrivate(false);
EXPECT_FALSE(id.Private());
}

/////////////////////////////////////////////////
Expand Down
150 changes: 149 additions & 1 deletion src/cmd/cmdfuel.rb.in
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ else
include Fiddle
end

require 'fileutils'
require 'optparse'
require 'uri'
require 'yaml'

# Constants.
LIBRARY_NAME = '@library_location@'
Expand Down Expand Up @@ -61,6 +64,7 @@ COMMANDS = { 'fuel' =>
" gz fuel [action] [options] \n"\
" \n"\
"Available Actions: \n"\
" configure Create config.yaml configuration file \n"\
" delete Delete resources \n"\
" download Download resources \n"\
" edit Edit a resource \n"\
Expand All @@ -80,6 +84,20 @@ COMMANDS = { 'fuel' =>
}

SUBCOMMANDS = {
'configure' =>
"Create `~/.gz/fuel/config.yaml` to hold Fuel server configurations. \n"\
" \n"\
" gz fuel configure [options] \n"\
" \n"\
" --defaults Use all the defaults and save. \n"\
" This will overwrite ~/.gz/fuel/config.yaml. \n"\
" --console Output to the console instead of to a file. \n"\
" -h [--help] Print this help message. \n"\
" \n"\
" --force-version <VERSION> Use a specific library version. \n"\
" \n"\
" --versions Show the available versions. \n",

'delete' =>
"Delete simulation resources \n"\
" \n"\
Expand Down Expand Up @@ -208,7 +226,9 @@ class Cmd
'pbtxt2config' => '',
'private' => '',
'onlymodels' => '0',
'onlyworlds' => '0'
'onlyworlds' => '0',
'defaults' => false,
'console' => false
}

usage = COMMANDS[args[0]]
Expand Down Expand Up @@ -276,6 +296,12 @@ class Cmd
opts.on('--onlyworlds', 'Only update worlds') do
options['onlyworlds'] = '1'
end
opts.on('--defaults', 'Use default values') do
options['defaults'] = true
end
opts.on('--console', 'Output to console') do
options['console'] = true
end
end # opt_parser do

opt_parser.parse!(args)
Expand Down Expand Up @@ -347,6 +373,12 @@ class Cmd
end # parse()

def execute(args)
# Graceful exit on ctrl-c
Signal.trap("SIGINT") do
puts "\nExiting"
exit
end

options = parse(args)

# Read the plugin that handles the command.
Expand Down Expand Up @@ -394,6 +426,8 @@ class Cmd
end

case options['subcommand']
when 'configure'
configure(options['defaults'], options['console'])
when 'delete'
Importer.extern 'int deleteUrl(const char *, const char *)'
if not Importer.deleteUrl(options['url'], options['header'])
Expand Down Expand Up @@ -463,4 +497,118 @@ class Cmd
"from #{plugin}."
end # begin
end # execute

# Runs `gz fuel configure`
def configure(defaults, console)
# Default fuel directory and configuration path
local_fuel_dir = File.join(Dir.home, '.gz', 'fuel')
config_path = File.join(local_fuel_dir, 'config.yaml')

# The default Fuel server URL
default_url = 'https://fuel.gazebosim.org'

# A lambda function that prompts the user to enter Fuel server information
get_server_info = lambda {
server_url = default_url
default_name = 'Fuel'

# Prompt the user for a server name with a default value
# Repeat until the URL is valid, or the user hits ctrl-c
until defaults
print "Fuel server URL [#{default_url}]: "
server_url = STDIN.gets.chomp
server_url = default_url if server_url.empty?
begin
uri = URI.parse(server_url)
default_name = !uri.host || uri.host.empty? ? uri.to_s : uri.host
break
rescue URI::InvalidURIError
puts 'Invalid URL.\n'
end
end

# Prompt the user for an access token with a default value of an
# empty string
access_token = ''
unless defaults
print 'Optional access token [None]: '
access_token = STDIN.gets.chomp
end

# Get the cache location
cache = local_fuel_dir
unless defaults
print "Local cache path [#{local_fuel_dir}]: "
cache = STDIN.gets.chomp
cache = local_fuel_dir if cache.empty?
end

# Get a name to associate with this server
server_name = default_name
unless defaults
print "Name this server [#{default_name}]: "
server_name = STDIN.gets.chomp
server_name = default_name if server_name.empty?
end

[server_name, server_url, access_token, cache]
}

unless console
puts '# Set Fuel server configurations.'
puts "# This will create or replace `#{config_path}`.\n\n"
end

servers = []
confirmation = 'n'
# Allow the user to enter multiple Fuel servers
begin
server_name, server_url, access_token, cache = get_server_info.call
servers << { 'name' => server_name,
'url' => server_url,
'private-token' => access_token,
'cache' => { 'path' => cache } }
unless defaults
print "\nAdd another Fuel server? [y/N]:"
confirmation = STDIN.gets.chomp.downcase
confirmation = 'n' if confirmation.empty?
end
end while confirmation == 'y'

unless defaults || console
puts "\nReview:\n"
servers.each do |server|
puts " Name: #{server['name']}"
puts " URL: #{server['url']}"
puts " Cache: #{server['cache']['path']}"
puts " Access token: #{server['private-token']}"
puts "\n" if servers.size > 1
end
end

confirmation = 'y'
unless defaults || console
print 'Save? [Y/n]:'
confirmation = STDIN.gets.chomp.downcase
confirmation = 'y' if confirmation.empty?
end

# Save settings
if confirmation == 'y'
config = {'servers' => servers}

if console
puts config.to_yaml
else
# Make sure the ~/.gz/fuel directory exists.
FileUtils.mkdir_p(local_fuel_dir)

# Write to the config.yaml file
File.open(config_path, 'w') { |file| file.write(config.to_yaml) }
puts 'Settings saved to ~/.gz/fuel/config.yaml.'
end
else
puts 'Settings not saved.'
end
end
end # class
21 changes: 19 additions & 2 deletions src/gz_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,9 @@ TEST(CmdLine, GZ_UTILS_TEST_ENABLED_ONLY_ON_LINUX(ListFail))
TEST(CmdLine,
GZ_UTILS_TEST_ENABLED_ONLY_ON_LINUX(ModelListConfigServerUgly))
{
auto output = custom_exec_str(g_listCmd + " -t model --raw");
EXPECT_NE(output.find("https://fuel.gazebosim.org/1.0/"),
auto output = custom_exec_str(g_listCmd +
" -t model --raw -u 'https://fuel.gazebosim.org' -o openrobotics");
EXPECT_NE(output.find("https://fuel.gazebosim.org"),
std::string::npos) << output;
EXPECT_EQ(output.find("owners"), std::string::npos) << output;
}
Expand Down Expand Up @@ -151,3 +152,19 @@ TEST(CmdLine,
EXPECT_NE(output.find("owners"), std::string::npos) << output;
EXPECT_NE(output.find("worlds"), std::string::npos) << output;
}

TEST(CmdLine,
GZ_UTILS_TEST_ENABLED_ONLY_ON_LINUX(ConfigureDefaultsConsole))
{
std::string output = custom_exec_str(
g_exec + " fuel configure --console --defaults");

std::string expected =
"---\n"
"servers:\n"
"- name: Fuel\n"
" url: https://fuel.gazebosim.org\n"
" private-token: ''\n";

EXPECT_EQ(output.find(expected), 0);
}
3 changes: 2 additions & 1 deletion src/gz_src_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ TEST_F(CmdLine, ModelListFail)
// https://github.com/gazebosim/gz-fuel-tools/issues/105
TEST_F(CmdLine, ModelListConfigServerUgly)
{
EXPECT_TRUE(listModels("", "openroboticstest", "true"));
EXPECT_TRUE(listModels("https://fuel.gazebosim.org",
"openroboticstest", "true"));

EXPECT_NE(this->stdOutBuffer.str().find("https://fuel.gazebosim.org"),
std::string::npos) << this->stdOutBuffer.str();
Expand Down
Loading

0 comments on commit e808b0a

Please sign in to comment.