Skip to content
Merged
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
35 changes: 20 additions & 15 deletions bin/mindee.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,29 @@
require_relative 'v1/parser'
require_relative 'v2/parser'

def setup_main_parser
v1_parser = MindeeCLI::V1Parser.new(ARGV)
v2_parser = MindeeCLI::V2Parser.new(ARGV)
main_parser = OptionParser.new do |opts|
opts.banner = "Usage: mindee [command]"
opts.separator "Commands:"
opts.separator " v1 Use Version 1 of the Mindee API"
opts.separator " v2 Use Version 2 of the Mindee API"
def root_help
help = "Usage: mindee command [options]\n\nAvailable commands:\n"
help += " #{'v1'.ljust(50)}Use Version 1 of the Mindee API\n"
help += " #{'search-models'.ljust(50)}Search for available models for this API key\n"

V2_PRODUCTS.each do |product_key, product_values|
help += " #{product_key.ljust(50)}#{product_values[:description]}\n"
end
main_command = ARGV.shift

case main_command
when 'v1'
v1_parser.execute
when 'v2'
v2_parser.execute
help
end

def setup_main_parser
main_command = ARGV.first

if main_command == 'v1'
ARGV.shift
MindeeCLI::V1Parser.new(ARGV).execute
elsif main_command.nil? || %w[help -h --help].include?(main_command)
abort(root_help)
else
abort(main_parser.help)
ARGV.shift if main_command == 'v2'
MindeeCLI::V2Parser.new(ARGV, command_prefix: 'mindee').execute
end
end

Expand Down
23 changes: 19 additions & 4 deletions bin/v2/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ class V2Parser
# @return [Parser]
attr_reader :search_parser

def initialize(arguments)
def initialize(arguments, command_prefix: 'mindee v2')
@arguments = arguments
@command_prefix = command_prefix
@options_parser = OptionParser.new do |opts|
opts.banner = 'Usage: mindee v2 command [options]'
opts.banner = "Usage: #{@command_prefix} command [options]"
end
@product_parser = init_product_parser
@search_parser = init_search_parser
Expand Down Expand Up @@ -67,6 +68,8 @@ def execute
else
abort("#{e.message}\n\n#{@product_parser[command].help}")
end
rescue Mindee::Error::MindeeError => e
abort(format_cli_error(e))
end

private
Expand All @@ -83,9 +86,21 @@ def validate_command!(command)
abort(error_msg)
end

def format_cli_error(error)
if error.is_a?(Mindee::Error::MindeeHTTPErrorV2) && error.status.to_i == 401
"CLI error: Missing credentials. Provide an API key using '--key' or " \
"the 'MINDEE_V2_API_KEY' environment variable."
elsif error.is_a?(Mindee::Error::MindeeAPIError) && error.message.include?('Missing API key')
"CLI error: Missing API key. Provide it using '--key' or " \
"the 'MINDEE_V2_API_KEY' environment variable."
else
"CLI error: #{error.message}"
end
end

def init_search_parser
OptionParser.new do |options_parser|
options_parser.banner = 'Usage: mindee v2 search-models [options]'
options_parser.banner = "Usage: #{@command_prefix} search-models [options]"
init_common_options(options_parser)
options_parser.on('-n [NAME]', '--name [NAME]',
'Search for partial matches in model name. Note: case insensitive') do |v|
Expand Down Expand Up @@ -159,7 +174,7 @@ def init_product_parser
v2_product_parser = {}
V2_PRODUCTS.each do |product_key, product_values|
v2_product_parser[product_key] = OptionParser.new do |options_parser|
options_parser.banner = "Usage: mindee v2 #{product_key} [options] file"
options_parser.banner = "Usage: #{@command_prefix} #{product_key} [options] file"
options_parser.on('-m MODEL_ID', '--model-id MODEL_ID', 'Model ID') { |v| @options[:model_id] = v }
options_parser.on('-a ALIAS', '--alias ALIAS', 'Add a file alias to the response') do |v|
@options[:alias] = v
Expand Down
16 changes: 8 additions & 8 deletions spec/bin/cli_integration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,40 +21,40 @@ def run_cli(*args)
context 'search-models command' do
['classification', 'crop', 'extraction', 'ocr', 'split'].each do |model_type|
it "returns model list for type #{model_type}" do
stdout, stderr, status = run_cli('v2', 'search-models', '-t', model_type)
stdout, stderr, status = run_cli('search-models', '-t', model_type)
expect(status.success?).to eq(true), stderr
expect(stdout.strip).not_to be_empty
end
end

it 'returns no models for non-existent name' do
stdout, stderr, status = run_cli('v2', 'search-models', '-n', 'supercalifragilisticexpialidocious')
stdout, stderr, status = run_cli('search-models', '-n', 'supercalifragilisticexpialidocious')
expect(status.success?).to eq(true), stderr
expect(stdout.strip).to eq('')
end

it 'returns models for name filter' do
stdout, stderr, status = run_cli('v2', 'search-models', '-n', 'findoc')
stdout, stderr, status = run_cli('search-models', '-n', 'findoc')
expect(status.success?).to eq(true), stderr
expect(stdout.strip).not_to be_empty
end

it 'returns models for name and model_type filters' do
stdout, stderr, status = run_cli('v2', 'search-models', '-n', 'findoc', '-t', 'extraction')
stdout, stderr, status = run_cli('search-models', '-n', 'findoc', '-t', 'extraction')
expect(status.success?).to eq(true), stderr
expect(stdout.strip).not_to be_empty
end

it 'returns HTTP 422 on invalid model type' do
stdout, stderr, status = run_cli('v2', 'search-models', '-t', 'invalid')
stdout, stderr, status = run_cli('search-models', '-t', 'invalid')
expect(status.success?).to eq(false)
expect("#{stdout}\n#{stderr}").to include('HTTP 422')
end
end

context 'product commands' do
it 'runs extraction from an URL source' do
stdout, stderr, status = run_cli('v2', 'extraction', '-m', findoc_model_id, blank_pdf_url)
stdout, stderr, status = run_cli('extraction', '-m', findoc_model_id, blank_pdf_url)
expect(status.success?).to eq(true), stderr
expect(stdout.strip).not_to be_empty
end
Expand All @@ -66,7 +66,7 @@ def run_cli(*args)
'split' => -> { split_model_id },
}.each do |command, model_id_proc|
it "runs #{command} with default args" do
stdout, stderr, status = run_cli('v2', command, '-m', instance_exec(&model_id_proc), test_file)
stdout, stderr, status = run_cli(command, '-m', instance_exec(&model_id_proc), test_file)
expect(status.success?).to eq(true), stderr
expect(stdout.strip).not_to be_empty
end
Expand All @@ -82,7 +82,7 @@ def run_cli(*args)
['-t', 'toto'],
].each do |option_args|
it "runs extraction with #{option_args.join(' ')}" do
stdout, stderr, status = run_cli('v2', 'extraction', '-m', findoc_model_id, test_file, *option_args)
stdout, stderr, status = run_cli('extraction', '-m', findoc_model_id, test_file, *option_args)
expect(status.success?).to eq(true), stderr
expect(stdout.strip).not_to be_empty
end
Expand Down
9 changes: 9 additions & 0 deletions spec/test_v1_cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ if [ "$RID" = "win-x64" ]; then
CLI_PATH="${CLI_PATH}.exe"
fi

echo "--- Test main menu includes v1 command"
HELP_OUTPUT=$("$CLI_PATH" 2>&1 || true)
if echo "$HELP_OUTPUT" | grep -q "v1"; then
echo "Main menu includes v1"
else
echo "Error: v1 command missing from main menu"
exit 1
fi

PRODUCTS="financial-document receipt invoice invoice-splitter"
PRODUCTS_SIZE=4
i=1
Expand Down
4 changes: 2 additions & 2 deletions spec/test_v2_cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ else
fi

echo "--- Test model list retrieval (all models)"
MODELS=$("$CLI_PATH" v2 search-models)
MODELS=$("$CLI_PATH" search-models)
if [ -z "$MODELS" ]; then
echo "Error: no models found"
exit 1
Expand All @@ -45,7 +45,7 @@ else
fi

echo "--- Test extraction with no additional args"
SUMMARY_OUTPUT=$("$CLI_PATH" v2 extraction -m "$MINDEE_V2_SE_TESTS_FINDOC_MODEL_ID" "$TEST_FILE")
SUMMARY_OUTPUT=$("$CLI_PATH" extraction -m "$MINDEE_V2_SE_TESTS_FINDOC_MODEL_ID" "$TEST_FILE")
if [ -z "$SUMMARY_OUTPUT" ]; then
echo "Error: no extraction output"
exit 1
Expand Down
31 changes: 31 additions & 0 deletions spec/v2/parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,35 @@
double_encoded = JSON.generate(JSON.generate(payload))
expect(parser.__send__(:raw_payload, double_encoded)).to eq(payload)
end
it 'formats auth API errors as a CLI credential message' do
cli_parser = described_class.new(['search-models'])
error = Mindee::Error::MindeeHTTPErrorV2.new(
{
'status' => 401,
'title' => 'Missing credentials',
'code' => '401-008',
'detail' => 'Credentials are required.',
'errors' => [],
}
)
allow(cli_parser).to receive(:validate_command!)
allow(cli_parser).to receive(:print_result).and_raise(error)

expect(cli_parser).to receive(:abort).with(
"CLI error: Missing credentials. Provide an API key using '--key' or " \
"the 'MINDEE_V2_API_KEY' environment variable."
).and_raise(SystemExit.new(1))

expect { cli_parser.execute }.to raise_error(SystemExit)
end

it 'prefixes generic Mindee errors as CLI errors' do
cli_parser = described_class.new(['search-models'])
allow(cli_parser).to receive(:validate_command!)
allow(cli_parser).to receive(:print_result).and_raise(Mindee::Error::MindeeAPIError, 'boom')

expect(cli_parser).to receive(:abort).with('CLI error: boom').and_raise(SystemExit.new(1))

expect { cli_parser.execute }.to raise_error(SystemExit)
end
end
Loading