# frozen_string_literal: truerequire'yaml'require'json'require'base64'classGitlab::CLI# Defines methods related to CLI output and formatting.moduleHelpersmodule_function# Returns actions available to CLI & Shell## @return [Array]defactions@actions||=Gitlab.actionsend# Returns Gitlab::Client instance## @return [Gitlab::Client]defclient@client||=Gitlab::Client.new(endpoint: Gitlab.endpoint||'')end# Returns method names and their owners## @return [Array<Hash>]defmethod_owners@method_owners||=actions.mapdo|action|{name: action.to_s,owner: client.method(action).owner.to_s}endend# Returns filtered required fields.## @return [Array]defrequired_fields(args)filtered_fields(args,'--only=')end# Returns filtered excluded fields.## @return [Array]defexcluded_fields(args)filtered_fields(args,'--except=')end# Returns fields filtered by a keyword.## @return [Array]deffiltered_fields(args,key)return[]unlessargs.any?&&args.last.is_a?(String)&&args.last.start_with?(key)args.last.gsub(key,'').split(',')end# Confirms command is valid.## @return [Boolean]defvalid_command?(cmd)command=cmd.is_a?(Symbol)?cmd:cmd.to_symGitlab.actions.include?(command)end# Confirms command with a desctructive action.## @return [String]defconfirm_command(cmd)returnunlesscmd.start_with?('remove_','delete_')puts'Are you sure? (y/n)'if%w[y yes].include?($stdin.gets.to_s.strip.downcase)puts'Proceeding..'elseputs'Command aborted.'exit(1)endend# Gets defined help for a specific command/action.## @return [String]defhelp(cmd=nil,&block)ifcmd.nil?||Gitlab::Help.help_map.key?(cmd)Gitlab::Help.actions_table(cmd)elseGitlab::Help.get_help(cmd,&block)endend# Outputs a nicely formatted table or error message.defoutput_table(cmd,args,data)casedatawhenGitlab::ObjectifiedHash,Gitlab::FileResponseputsrecord_table([data],cmd,args)whenGitlab::PaginatedResponseputsrecord_table(data,cmd,args)else# probably just an error messageputsdataendenddefoutput_json(cmd,args,data)ifdata.respond_to?(:empty?)&&data.empty?puts'{}'elsehash_result=casedatawhenGitlab::ObjectifiedHash,Gitlab::FileResponserecord_hash([data],cmd,args,single_value: true)whenGitlab::PaginatedResponserecord_hash(data,cmd,args)else{cmd: cmd,data: data,args: args}endputsJSON.pretty_generate(hash_result)endend# Table to display records.## @return [Terminal::Table]defrecord_table(data,cmd,args)return'No data'ifdata.empty?arr,keys=get_keys(args,data)tabledo|t|t.title="Gitlab.#{cmd}#{args.join(', ')}"t.headings=keysarr.each_with_indexdo|hash,index|values=[]keys.eachdo|key|casevalue=hash[key]whenHashvalue=value.key?('id')?value['id']:'Hash'whenStringIOvalue='File'whennilvalue='null'endvalues<<valueendt.add_rowvaluest.add_separatorunlessarr.size-1==indexendendend# Renders the result of given commands and arguments into a Hash## @param [Array] data Resultset from the API call# @param [String] cmd The command passed to the API# @param [Array] args Options passed to the API call# @param [bool] single_value If set to true, a single result should be returned# @return [Hash] Result hashdefrecord_hash(data,cmd,args,single_value: false)ifdata.empty?result=nilelsearr,keys=get_keys(args,data)result=[]arr.eachdo|hash|row={}keys.eachdo|key|row[key]=casehash[key]whenHash'Hash'whenStringIOBase64.encode64(hash[key].read)whennilnilelsehash[key]endendresult.pushrowendresult=result[0]ifsingle_value&&result.count.positive?end{cmd: "Gitlab.#{cmd}#{args.join(', ')}".strip,result: result}end# Helper function to get rows and keys from data returned from API calldefget_keys(args,data)arr=data.map(&:to_h)keys=arr.first.keys.sort_by(&:to_s)keys&=required_fields(args)ifrequired_fields(args).any?keys-=excluded_fields(args)[arr,keys]end# Helper function to call Gitlab commands with args.defgitlab_helper(cmd,args=[])args.any??Gitlab.send(cmd,*args):Gitlab.send(cmd)rescueStandardError=>eputse.messageyieldifblock_given?end# Convert a hash (recursively) to use symbol hash keys# @return [Hash]defsymbolize_keys(hash)ifhash.is_a?(Hash)hash=hash.each_with_object({})do|(key,value),new_hash|new_hash[key.to_sym]=symbolize_keys(value)rescueNoMethodErrorraise"Error: cannot convert hash key to symbol: #{key}"endendhashend# Check if arg is a color in 6-digit hex notation with leading '#' signdefhex_color?(arg)pattern=/\A#\h{6}\Z/pattern.match(arg)end# YAML::load on a single argumentdefyaml_load(arg)hex_color?(arg)?arg:YAML.safe_load(arg)rescuePsych::SyntaxErrorraise"Error: Argument is not valid YAML syntax: #{arg}"endendend