diff --git a/container/src/finance/lib/internal/lib_fetch.bash b/container/src/finance/lib/internal/lib_fetch.bash index 94d0920..b42c16f 100644 --- a/container/src/finance/lib/internal/lib_fetch.bash +++ b/container/src/finance/lib/internal/lib_fetch.bash @@ -352,6 +352,7 @@ function lib_fetch::__parse_args() fi } +# TODO: complete __fetch() rewrite function lib_fetch::__fetch() { # Supported remote fetch accounts @@ -368,15 +369,17 @@ function lib_fetch::__fetch() "pera-wallet" ) + [ -z "$global_parent_profile" ] && lib_utils::die_fatal + [ -z "$global_child_profile" ] && lib_utils::die_fatal + # TODO: global args should be set in arg parsing and made readonly # Fetch only given accounts if [ ! -z "$global_arg_account" ]; then for _account in "${global_arg_account[@]}"; do - if ! lib_fetch::__parse_yaml "get-value" "account.${_account}" 1 &>/dev/null; then - # Unsupported or unavailable - [ -z "$global_parent_profile" ] && lib_utils::die_fatal - [ -z "$global_child_profile" ] && lib_utils::die_fatal - lib_utils::print_warning "${global_parent_profile}.${global_child_profile}.account.${_account} not found, skipping!" + local _value + _value="$(lib_fetch::__parse_yaml "get-value" "account.${_account}" 2>/dev/null)" + if [[ "$_value" == "null" ]]; then + lib_utils::print_warning "account.${_account} not found, skipping!" else local _accounts+=("$_account") fi @@ -442,39 +445,49 @@ function lib_fetch::__fetch_account() local _members=("key" "passphrase" "secret" "subaccount") for _member in "${_members[@]}"; do + # Get member value - if _value=$(lib_fetch::__parse_yaml "get-value" "account.${_account}.${_member}"); then + local _value + _value=$(lib_fetch::__parse_yaml "get-values" "account.${_account}.${_member}") - # Member value for key - if [[ "$_member" == "key" ]]; then - local _api_key="$_value" - if [[ -z "$_api_key" && $_need_key == true ]]; then - lib_utils::die_fatal "$_account - empty fetch ${_member}!" - fi + if [ $? -eq 0 ]; then + + # Sanitize for 'yq'-specific caveats + local _sanitized + if [[ "$_value" == "null" ]]; then + _sanitized="" + else + # Values may contain 'yq'-interpreted characters (newlines, etc.) + # NOTE: this is needed for at least Coinbase's CDP secret + _sanitized="$(echo "$_value" | sed -e 's:^[ \t]*::' -e '/^$/d' -e "s:^'::" -e "s:'$::")" fi - # Member value for passphrase - if [[ "$_member" == "passphrase" ]]; then - local _api_passphrase="$_value" - if [[ -z "$_api_passphrase" && $_need_passphrase == true ]]; then - lib_utils::die_fatal "$_account - empty fetch ${_member}!" - fi - fi - - # Member value for secret - if [[ "$_member" == "secret" ]]; then - local _api_secret="$_value" - if [[ -z "$_api_secret" && $_need_secret == true ]]; then - lib_utils::die_fatal "$_account - empty fetch ${_member}!" - fi - fi - - # Member value for subaccount (will *always* need subaccount) - if [[ "$_member" == "subaccount" ]]; then - local _api_subaccount="$_value" - [ -z "$_api_subaccount" ] \ - && lib_utils::die_fatal "$_account - empty fetch ${_member}!" - fi + case "$_member" in + key) + local _api_key="$_sanitized" + [[ -z "$_api_key" && $_need_key == true ]] \ + && lib_utils::die_fatal "$_account - empty fetch ${_member}!" + ;; + passphrase) + local _api_passphrase="$_sanitized" + [[ -z "$_api_passphrase" && $_need_passphrase == true ]] \ + && lib_utils::die_fatal "$_account - empty fetch ${_member}!" + ;; + secret) + local _api_secret="$_sanitized" + [[ -z "$_api_secret" && $_need_secret == true ]] \ + && lib_utils::die_fatal "$_account - empty fetch ${_member}!" + ;; + subaccount) + # NOTE: member value for subaccount will *always* need subaccount + local _api_subaccount="$_sanitized" + [ -z "$_api_subaccount" ] \ + && lib_utils::die_fatal "$_account - empty fetch ${_member}!" + ;; + *) + lib_utils::die_fatal "Unsupported member" + ;; + esac else lib_utils::print_warning "$_account is unavailable, skipping!" continue 2 @@ -494,6 +507,7 @@ function lib_fetch::__fetch_account() # (we'll pass entire string to internal fetch impl) # TODO: parse/cut before setting var [ -z "$global_child_profile_flow" ] && lib_utils::die_fatal + local _sub _sub="$(echo $_api_subaccount | cut -d'/' -f1)" local _api_out_dir="${global_child_profile_flow}/import/${global_child_profile}/${_account}/${_sub}/1-in/${global_arg_year}" @@ -508,9 +522,8 @@ function lib_fetch::__fetch_account() # TODO: clarify, add more/better comments - # If subaccount is type 'struct', then subaccount will be list of addresses - # So, create what's needed for internal fetch impl (blockchain:address format) - if [[ $(lib_fetch::__parse_yaml "get-type" "account.${_account}.subaccount") == "struct" ]]; then + # If subaccount is list of addresses, create 'blockchain:address' format (for internal fetch impl) + if echo "$_api_subaccount" | head -n2 | grep ":*$" | tail -n1 | grep -q "^- "; then # Parse out the separate blockchains local _blockchain @@ -520,7 +533,7 @@ function lib_fetch::__fetch_account() _parsed_csv=$( echo "$_blockchain" | while read _key; do local _addresses - _addresses=$(echo "$_api_subaccount" | shyaml get-value "$_key") # TODO: use lib + _addresses=$(echo "$_api_subaccount" | yq -M -y --indentless -e ".${_key}") # TODO: use lib echo "$_addresses" | while read _value; do echo "${_key}:${_value}" | sed -e 's/:- /\//g' -e "s:'::g" done @@ -663,20 +676,24 @@ function lib_fetch::__fetch_price() local _members=("key" "asset") for _member in "${_members[@]}"; do - if _value=$(lib_fetch::__parse_yaml "get-value" "price.${_api}.${_member}"); then - if [[ "$_member" == "key" ]]; then - [ -z "$_value" ] \ - && lib_utils::print_warning "${_api} has empty ${_member}" - fi + local _value + _value="$(lib_fetch::__parse_yaml "get-value" "price.${_api}.${_member}" 2>/dev/null)" + + case "$_member" in + key) + [[ "$_value" == "null" ]] && lib_utils::print_warning "${_api} has empty ${_member}" + ;; + asset) + if ! lib_fetch::__parse_yaml "get-value" "price.${_api}.${_member}" | grep -q "^- "; then + lib_utils::die_fatal "Invalid asset sequence!" + fi + ;; + *) + lib_utils::die_fatal "${_api} has invalid member: ${_member}" + ;; + esac - if [[ "$_member" == "asset" ]]; then - [[ $(lib_fetch::__parse_yaml "get-type" "price.${_api}.${_member}") != "sequence" ]] \ - && lib_utils::die_fatal "Invalid asset sequence!" - fi - else - lib_utils::die_fatal "'${_member}' is unavailable" - fi done # Available API(s) @@ -707,7 +724,7 @@ function lib_fetch::__fetch_price() # Parse out each asset while read _line; do local _assets+=("$_line") - done < <(lib_fetch::__parse_yaml "get-values" "price.${_api}.asset") + done < <(lib_fetch::__parse_yaml "get-values" "price.${_api}.asset" | sed 's:^- ::') else # Get assets from CLI local _assets=("${global_arg_price[*]}") @@ -766,14 +783,34 @@ function lib_fetch::__fetch_price() function lib_fetch::__parse_yaml() { + [[ -z "$1" || -z "$2" ]] && lib_utils::die_fatal local _action="$1" local _append="$2" - [ -z "$_action" ] && lib_utils::die_fatal - [ -z "$_append" ] && lib_utils::die_fatal [ -z "$global_conf_fetch" ] && lib_utils::die_fatal - echo "$(<$global_conf_fetch)" \ - | shyaml -q "$_action" "${global_parent_profile}"."${global_child_profile}"."${_append}" + local -r _raw=".${global_parent_profile}.${global_child_profile}.${_append}" + + # `yq` (kislyuk's) requires quotes around keys/members that contain '-' + local _arg="$_raw" + [[ "$_arg" =~ '-' ]] && _arg="$(sed -e 's:\.:"\.":g' -e 's:"\.:\.:' -e "s: *$:\":" <(echo "$_raw"))" + + # Pseudo-substitution of `shyaml` functionality + local _ifs="$IFS" + IFS=' ' + local -r _cmd=("yq" "-M" "-y" "--indentless" "-e" "$_arg" "$global_conf_fetch") + local -r _grep=("grep" "-v" "^\.\.\.$") + case "$_action" in + get-value) + "${_cmd[@]}" | "${_grep[@]}" -m2 + ;; + get-values) + "${_cmd[@]}" | "${_grep[@]}" + ;; + *) + lib_utils::die_fatal "Unsupported YAML action" + ;; + esac + IFS="$_ifs" } function lib_fetch::__fetch_exec()