diff --git a/Gemfile b/Gemfile index 21b1aa0ca10..f13db8b387a 100644 --- a/Gemfile +++ b/Gemfile @@ -80,6 +80,7 @@ gem 'webauthn', '~> 2.5.2' gem 'xmldsig', '~> 0.6' gem 'xmlenc', '~> 0.7', '>= 0.7.1' gem 'yard', require: false +gem 'zlib', require: false # This version of the zxcvbn gem matches the data and behavior of the zxcvbn NPM package. # It should not be updated without verifying that the behavior still matches JS version 4.4.2. diff --git a/Gemfile.lock b/Gemfile.lock index 424f4cf061b..4b04e5cc7e1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -702,6 +702,7 @@ GEM nokogiri (~> 1.8) yard (0.9.34) zeitwerk (2.6.12) + zlib (3.0.0) zonebie (0.6.1) zxcvbn (0.1.9) @@ -825,6 +826,7 @@ DEPENDENCIES xmldsig (~> 0.6) xmlenc (~> 0.7, >= 0.7.1) yard + zlib zonebie zxcvbn (= 0.1.9) diff --git a/lib/script_base.rb b/lib/script_base.rb index 379365f83fb..88ca8975b95 100644 --- a/lib/script_base.rb +++ b/lib/script_base.rb @@ -25,10 +25,12 @@ def initialize(argv:, stdout:, stderr:, subtask_class:, banner:) :format, :show_help, :requesting_issuers, + :deflate, keyword_init: true, ) do alias_method :include_missing?, :include_missing alias_method :show_help?, :show_help + alias_method :deflate?, :deflate end def config @@ -37,6 +39,7 @@ def config format: :table, show_help: false, requesting_issuers: [], + deflate: false, ) end @@ -59,13 +62,23 @@ def run stderr.puts "*Messages*:\n#{result.messages.map { |message| " • #{message}" }.join("\n")}" end - if result.json + if config.deflate? + require 'zlib' + require 'base64' + stdout.puts Base64.encode64( + Zlib::Deflate.deflate( + (result.json || result.table).to_json, + Zlib::BEST_COMPRESSION, + ), + ) + elsif result.json stdout.puts result.json.to_json else self.class.render_output(result.table, format: config.format, stdout: stdout) end end + # rubocop:disable Metrics/BlockLength def option_parser @option_parser ||= OptionParser.new do |opts| opts.banner = banner @@ -92,6 +105,10 @@ def option_parser config.format = :json end + opts.on('--deflate', 'Use DEFLATE compression on the output') do + config.deflate = true + end + opts.on('--[no-]include-missing', <<~STR) do |include_missing| Whether or not to add rows in the output for missing inputs, defaults to on STR @@ -99,6 +116,7 @@ def option_parser end end end + # rubocop:enable Metrics/BlockLength # @param [Array>] rows def self.render_output(rows, format:, stdout: STDOUT) diff --git a/spec/lib/script_base_spec.rb b/spec/lib/script_base_spec.rb new file mode 100644 index 00000000000..d3bb75e22c5 --- /dev/null +++ b/spec/lib/script_base_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' +require 'script_base' + +RSpec.describe ScriptBase do + let(:subtask_class) do + Class.new do + def run(args:, config:) # rubocop:disable Lint/UnusedMethodArgument + ScriptBase::Result.new( + table: [ + %w[header1 header2], + %w[value1 value2], + ], + subtask: 'example-subtask', + uuids: %w[a b c], + ) + end + end + end + + let(:stdout) { StringIO.new } + let(:stderr) { StringIO.new } + + describe '#run' do + let(:argv) { [] } + + subject(:base) do + ScriptBase.new( + argv:, + stdout:, + stderr:, + subtask_class:, + banner: '', + ) + end + + context 'with --deflate' do + let(:argv) { %w[--deflate] } + + it 'applies DEFLATE compression to the output' do + base.run + + table = subtask_class.new.run(args: nil, config: nil).table + + expect(JSON.parse(Zlib::Inflate.inflate(Base64.decode64(stdout.string)))).to eq(table) + end + end + end +end