diff --git a/.gitignore b/.gitignore index 387cdb2..203be5a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ .ovhConsumerKey* libs/ profile/ - +contrib/test/*.json diff --git a/README.md b/README.md index 55c6e79..865f7e8 100644 --- a/README.md +++ b/README.md @@ -75,43 +75,7 @@ To create a Consumer key for different account or usage (profile is created if m ./ovh-api-bash-client.sh --profile demo2 --init ``` - Embedded lib for external scripts ----------- +--------------------------------- -### ovh-api-lib.sh - -#### OvhRequestApi - -OvhRequestApi() is wrapper to ovh-api-bash-client.sh - -``` - OvhRequestApi url [method] [post_data] -``` - -return values in OVHAPI_HTTP_STATUS and OVHAPI_HTTP_RESPONSE - - -#### wrappers for JSON.sh - -- getJSONKeys() : get JSON keys, remove first/last double quotes if present -- getJSONValue() : get a JSON key value, remove first/last double quotes if present -- getJSONValues() : get all JSON values at once - -### sample usage - -Once you've an available OVH API authentication, you can use the library : - -- To override profile, set OVHAPI_BASHCLIENT_PROFILE -- To override target, set OVHAPI_TARGET -- For **ovh-api-lib.sh** debug output, set OVHAPILIB_DEBUG to 1 - -This variables can be set in your script or exported from commandline - -You can find some samples scripts in the **samples/** directory - -**sample usage** - -``` - OVHAPI_BASHCLIENT_PROFILE=demo samples/list-domains.sh -``` +See **contrib/** directory diff --git a/contrib/README.md b/contrib/README.md new file mode 100644 index 0000000..a6e5777 --- /dev/null +++ b/contrib/README.md @@ -0,0 +1,104 @@ +## Embedded libs for external scripts + +### jsonsh-lib.sh + +#### Introduction + +Wrapper for JSON.sh, enhancing output : +- action on keys : transform original key (JSON array) to a friendly format +- action on values : trim double quotes and spaces + +**original JSON content** +``` +{ + "person": { + "name": "Foobar1", + "foo1": { "bar": "baz1" }, + "child": [ {"name":"bob1_1"}, {"name":"bob1_2"} ] + }, + "person": { + "name": "Foobar2", + "foo1": { "bar": "baz2" }, + "child": [ {"name":"bob2_2"}, {"name":"bob2_2"} ] + } +} +``` + +**JSON.sh output** +``` +["person","name"] "Foobar1" +["person","foo1","bar"] "baz1" +["person","child",0,"name"] "bob1_1" +["person","child",1,"name"] "bob1_2" +["person","name"] "Foobar2" +["person","foo1","bar"] "baz2" +["person","child",0,"name"] "bob2_2" +["person","child",1,"name"] "bob2_2" +``` + +**wrapper output** +``` +person.name Foobar1 +person.foo1.bar baz1 +person.child[0].name bob1_1 +person.child[1].name bob1_2 +person.name Foobar2 +person.foo1.bar baz2 +person.child[0].name bob2_2 +person.child[1].name bob2_2 +``` + +#### Functions + +- loadJSON() : forward JSON content to JSON.sh script if JSON is set, otherwise print JSON.sh output) +- getJSONKeys() : print JSON keys +- getJSONValue() : print JSON key's value +- getJSONValues() : print full result (json key and value) + +JSON.sh is called only one time + +From your script or commandline, you can set : +- JSONSH_DIR if JSON.sh is not installed on your system path (should be set before including the lib) +- JSONSH_SEPARATOR if you want a custom separator between JSON key and value (default is ":") +- JSONSHLIB_DEBUG to 1 to enable lib debugging + +#### Samples + +See **test/** directory : + +``` +JSONSHLIB_DEBUG=1 ./test/jsonshlib_test.sh +``` + +### ovh-api-lib.sh + +- OvhRequestApi() is wrapper for ovh-api-bash-client.sh +- use jsonsh-lib.sh ( just using loadJSON() ) + +**usage** +``` +OvhRequestApi url [method] [post_data] +``` + +From your script or commandline, you can set : +- OVHAPI_BASHCLIENT_PROFILE +- OVHAPI_TARGET +- OVHAPILIB_DEBUG to 1 to enable lib debugging + +This function set variables OVHAPI_HTTP_STATUS and OVHAPI_HTTP_RESPONSE + +OVHAPI_HTTP_RESPONSE is forwarded to loadJSON() to avoid user to put this line each time. + +#### Samples + +Once you've a valid OVH API authentication, you can use the library + +You can find some samples scripts in the **samples/** directory + +**sample usage** + +``` +OVHAPI_BASHCLIENT_PROFILE=demo samples/list-domains.sh +``` + +See **samples/** directory for more details diff --git a/contrib/jsonsh-lib.sh b/contrib/jsonsh-lib.sh new file mode 100644 index 0000000..028f916 --- /dev/null +++ b/contrib/jsonsh-lib.sh @@ -0,0 +1,212 @@ +#!/usr/bin/env bash + +# +# Lib for parsing https://github.com/dominictarr/JSON.sh output +# + +JSONSH_CACHE= +JSONSH_SOURCE_MD5= +JSONSH_SEPARATOR= + +JSONSHLIB_DEBUG=${JSONSHLIB_DEBUG:-0} + +### detect JSON.sh location +# JSON.sh searched in system path +readonly JSONSH_SYSTEM_DIR=$(dirname "$(which JSON.sh 2>/dev/null)" ) + +# can be overrided +JSONSH_DIR=${JSONSH_DIR:-"${JSONSH_SYSTEM_DIR}"} + +if [ -z "${JSONSH_DIR}" ]; then + echo "JSONSH_DIR should be set" >&2 + exit 1 +else + # to get absolte path + JSONSH_DIR=$(cd "${JSONSH_DIR}" && pwd) + if [ ! -f "${JSONSH_DIR}/JSON.sh" ]; then + echo "${JSONSH_DIR}/JSON.sh not found" >&2 + exit 1 + fi +fi + +readonly JSONSH_DIR + +# debug output if wanted +_jsonshlib_echo_debug() +{ + if [ "${JSONSHLIB_DEBUG}" == "1" ]; then + echo "[debug:${FUNCNAME[1]}] $*" >&2 + fi +} + +# +# single entry point with JSON.sh +# load json defined as argument, and set result to JSONSH_CACHE +# +# keep result in cache to avoid useless calls to JSON.sh +# +# usage : +# to set source json : loadJSON "json content" +# to get JSON.sh output, don't set argument +# +loadJSON() +{ + local json_source="$1" + local current_md5= + + if [ -z "${json_source}" ]; then + if [ -z "${JSONSH_CACHE}" ]; then + echo "JSON content is empty" >&2 + exit 1 + fi + _jsonshlib_echo_debug "get JSON.sh result from cache" + echo "${JSONSH_CACHE}" + else + # only follow to JSON.sh if JSon content differs + current_md5=$(echo "${json_source}" | md5sum | cut -d ' ' -f1) + if [ "${JSONSH_SOURCE_MD5}" != "${current_md5}" ]; then + _jsonshlib_echo_debug "new JSON source, build JSON.sh cache" + JSONSH_SOURCE_MD5=${current_md5} + JSONSH_CACHE=$("${JSONSH_DIR}/JSON.sh" -l <<< "${json_source}") + fi + fi + + return 0 + +} + +# +# convert JSON.sh key output format (JSON array) and trim value, through pipe +# +# sample : +# json.sh output : ["foo","bar",0,"baz"] " json value " +# new output : foo.bar[0].baz json value +# +# for each value, outside double quotes and spaces are removed +# +# _JSonSH_rewrite_output getKeys : print only keys +# _JSonSH_rewrite_output getValue : print only value for the field +# _JSonSH_rewrite_output getFull : print pair of key/value +# +# separator between key and value can be overrided if JSONSH_SEPARATOR is set (default = ":") +# +_JSonSH_rewrite_output() +{ + local action=$1 + local wanted_key=$2 + + if [[ "${action}" == "getValue" ]] && [[ -z "${wanted_key}" ]]; then + echo "key is required" >&2 + exit 1 + fi + + JSONSH_SEPARATOR=${JSONSH_SEPARATOR:-":"} + + awk -F '\t' \ + -v action="${action}" \ + -v wanted_key="${wanted_key}" \ + -v separator="${JSONSH_SEPARATOR}" \ + '{ + json_key = $1 + # drop the key from the line + $1 = "" + json_value=$0 + + ## Actions on json key : + # 1) remove some chars : brackets and double quotes + gsub(/\[|\]|\"/,"",json_key) + # 2) detect array index between comma, put digits between brackets + json_key = gensub(/(,([[:digit:]]+)(,|))/,"[\\2]\\3","g",json_key) + # 3) replace each comma with dot + gsub(/,/,".",json_key) + + ## Actions on json value : + # remove first/last double quotes if present + json_value = gensub(/"(.*)"$/,"\\1","g",json_value) + # trim first/last spaces of value + gsub(/^\s+|\s+$/,"",json_value) + + switch (action) { + case "getKeys": + print json_key + break + case "getValue": + # remove first/last double quotes if present + wanted_key = gensub(/"(.*)"$/,"\\1","g",wanted_key) + # the value for a key is wanted + if (json_key == wanted_key) + { + # display value if found and stop + print json_value + exit 0 + } + break + case "getFull": + if (json_key ~ /^[0-9]+$/ ) + { + # the key is a number, only value is needed + print json_value + } else { + # the key is a string, display key and value + print json_key separator json_value + } + break + default: + print "Bad action !" >"/dev/stderr" + exit 1 + break + } + }' &2 + fi +} + +# to override profile, define value in the variable OVHAPI_BASHCLIENT_PROFILE +# to override target, define value in the variable OVHAPI_TARGET +# OvhRequestApi url [method] [post_data] +# +# default method: get +# return response code in OVHAPI_HTTP_STATUS and content in OVHAPI_HTTP_RESPONSE +OvhRequestApi() +{ + local url=$1 + local method=$2 + local data=$3 + + local client_response= + + local cmd=(${OVHAPI_BASHCLIENT_BIN}) + + ## construct arguments array + if [ -n "${OVHAPI_BASHCLIENT_PROFILE}" ]; then + cmd+=(--profile ${OVHAPI_BASHCLIENT_PROFILE}) + fi + + if [ -n "${url}" ]; then + cmd+=(--url ${url}) + fi + + if [ -n "${method}" ]; then + cmd+=(--method ${method}) + fi + + if [ -n "${OVHAPI_TARGET}" ]; then + cmd+=(--target ${OVHAPI_TARGET}) + fi + + if [ "${method}" == "POST" ]; then + # double-quote data content for bash input + data=$(printf "%q" "${data}") + cmd+=(--data ${data}) + fi + + _ovhapilib_echo_debug "command: ${cmd[*]}" + + # best way found to correctly pass quoted arguments to a command called from a function + client_response=$(echo "${cmd[*]}" | bash) + + OVHAPI_HTTP_STATUS=$(echo "${client_response}" | cut -d ' ' -f1) + OVHAPI_HTTP_RESPONSE="$(echo "${client_response}" | cut -d ' ' -f2-)" + + _ovhapilib_echo_debug "http_status=${OVHAPI_HTTP_STATUS}" + + # forward result to JSON.sh to be usable with JSONSH functions + # (avoid user to put this line each time) + loadJSON "${OVHAPI_HTTP_RESPONSE}" + +} diff --git a/samples/list-domains.sh b/contrib/samples/list-domains.sh similarity index 73% rename from samples/list-domains.sh rename to contrib/samples/list-domains.sh index e9977d8..f9bb0ab 100755 --- a/samples/list-domains.sh +++ b/contrib/samples/list-domains.sh @@ -2,7 +2,7 @@ HERE=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) source ${HERE}/../ovh-api-lib.sh || exit 1 -OvhRequestApi "/me" +OvhRequestApi /me if [ "${OVHAPI_HTTP_STATUS}" != "200" ]; then echo "profile error:" @@ -13,7 +13,7 @@ fi OvhRequestApi "/domain" if [ "${OVHAPI_HTTP_STATUS}" -eq 200 ]; then - domains=($(getJSONValues "${OVHAPI_HTTP_RESPONSE}")) + domains=($(getJSONValues)) echo "number of domains=${#domains[@]}" # for example, only list for first domain @@ -24,9 +24,9 @@ if [ "${OVHAPI_HTTP_STATUS}" -eq 200 ]; then OvhRequestApi "/domain/${domain}" echo "-- single value --" # key can be passed with/without double quote - getJSONValue "${OVHAPI_HTTP_RESPONSE}" lastUpdate - getJSONValue "${OVHAPI_HTTP_RESPONSE}" '"transferLockStatus"' + getJSONValue lastUpdate + getJSONValue '"transferLockStatus"' echo "-- get all values --" - getJSONValues "${OVHAPI_HTTP_RESPONSE}" + getJSONValues done fi diff --git a/samples/my-informations.sh b/contrib/samples/my-informations.sh similarity index 60% rename from samples/my-informations.sh rename to contrib/samples/my-informations.sh index 9e6d46b..03817ad 100755 --- a/samples/my-informations.sh +++ b/contrib/samples/my-informations.sh @@ -10,9 +10,9 @@ if [ "${OVHAPI_HTTP_STATUS}" != "200" ]; then exit else echo "-- all fields --" - getJSONValues "${OVHAPI_HTTP_RESPONSE}" + getJSONValues echo "-- only some fields --" - getJSONValue "${OVHAPI_HTTP_RESPONSE}" "email" - getJSONValue "${OVHAPI_HTTP_RESPONSE}" "currency.code" - getJSONValue "${OVHAPI_HTTP_RESPONSE}" "currency.symbol" + getJSONValue "email" + getJSONValue "currency.code" + getJSONValue "currency.symbol" fi diff --git a/samples/post-domain-TXT-record.sh b/contrib/samples/post-domain-TXT-record.sh similarity index 87% rename from samples/post-domain-TXT-record.sh rename to contrib/samples/post-domain-TXT-record.sh index e58ae24..d2711c2 100755 --- a/samples/post-domain-TXT-record.sh +++ b/contrib/samples/post-domain-TXT-record.sh @@ -2,7 +2,7 @@ HERE=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) source ${HERE}/../ovh-api-lib.sh || exit 1 -OvhRequestApi "/me" +OvhRequestApi /me if [ "${OVHAPI_HTTP_STATUS}" != "200" ]; then echo "profile error:" @@ -15,7 +15,7 @@ if [ -z "${OVH_DOMAIN}" ]; then echo -e "choose in :\n" OvhRequestApi "/domain" - getJSONValues "${OVHAPI_HTTP_RESPONSE}" + getJSONValues exit 1 fi @@ -31,7 +31,7 @@ EOF OvhRequestApi "/domain/zone/${OVH_DOMAIN}/record/" POST "${CUSTOMDATA}" echo ${OVHAPI_HTTP_STATUS} -getJSONValues "${OVHAPI_HTTP_RESPONSE}" +getJSONValues txt_value="test2: text with space and quo't'es" CUSTOMDATA="{\"fieldType\":\"TXT\",\"subDomain\":\"${txt_field}\",\"target\":\"${txt_value}\",\"ttl\":0}" @@ -39,4 +39,4 @@ CUSTOMDATA="{\"fieldType\":\"TXT\",\"subDomain\":\"${txt_field}\",\"target\":\"$ OvhRequestApi "/domain/zone/${OVH_DOMAIN}/record/" POST "${CUSTOMDATA}" echo ${OVHAPI_HTTP_STATUS} -getJSONValues "${OVHAPI_HTTP_RESPONSE}" +getJSONValues diff --git a/contrib/test/jsonshlib_test.sh b/contrib/test/jsonshlib_test.sh new file mode 100755 index 0000000..a528e07 --- /dev/null +++ b/contrib/test/jsonshlib_test.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +HERE=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) + +JSONSH_DIR=${HERE}/../../libs/ +source ${HERE}/../../contrib/jsonsh-lib.sh || exit 1 + +# optional output separator +JSONSH_SEPARATOR="\t:\t" + +_pause() +{ + echo -e "\n== $1 ==\n" + read -p "-- Press ENTER --" +} + +_pause "demo file" +JSON=$(cat <&2 - fi -} - - -# remove first and last double quote from string -_trimDoubleQuotes() -{ - local value="$1" - - [ -z "${value}" ] && return - - value="${value%\"}" - value="${value#\"}" - echo "${value}" -} - -# to override profile, define value in the variable OVHAPI_BASHCLIENT_PROFILE -# to override target, define value in the variable OVHAPI_TARGET -# OvhRequestApi url [method] [post_data] -# -# default method: get -# return response code in OVHAPI_HTTP_STATUS and content in OVHAPI_HTTP_RESPONSE -OvhRequestApi() -{ - local url=$1 - local method=$2 - local data=$3 - - local client_response= - - local cmd=(${OVHAPI_BASHCLIENT_BIN}) - - ## construct arguments array - if [ -n "${OVHAPI_BASHCLIENT_PROFILE}" ]; then - cmd+=(--profile ${OVHAPI_BASHCLIENT_PROFILE}) - fi - - if [ -n "${url}" ]; then - cmd+=(--url ${url}) - fi - - if [ -n "${method}" ]; then - cmd+=(--method ${method}) - fi - - if [ -n "${OVHAPI_TARGET}" ]; then - cmd+=(--target ${OVHAPI_TARGET}) - fi - - if [ "${method}" == "POST" ]; then - # double-quote data content for bash input - data=$(printf "%q" "${data}") - cmd+=(--data ${data}) - fi - - _ovhapilib_echo_debug "command: ${cmd[*]}" - - # best way found to correctly pass quoted arguments to a command called from a function - client_response=$(echo "${cmd[*]}" | bash) - - OVHAPI_HTTP_STATUS=$(echo "${client_response}" | cut -d ' ' -f1) - OVHAPI_HTTP_RESPONSE="$(echo "${client_response}" | cut -d ' ' -f2-)" - - _ovhapilib_echo_debug "http_status=${OVHAPI_HTTP_STATUS}" - -} - - - -## vendor's JSON.sh parsing functions - -# usage : getJSONKeys "json" -# -# return JSON keys list without double quote if present -getJSONKeys() -{ - local json="$1" - local json_key= - - echo "${json}" \ - | "${OVHAPI_BASHCLIENT_LIB}/JSON.sh" -l \ - | sed -r "s/\[(.+)\]\s+(.*)/\1/" \ - | while read -r json_key - do - # replacement for key with nested object - json_key=${json_key/\",\"/.} - _trimDoubleQuotes "${json_key}" - done -} - - -# usage : getJSONValue "json" field -# -# if field is a string, it can be set with/without double quotes -# if the result is between double quote, only get the value inside -getJSONValue() -{ - local json="$1" - local field="$2" - local result= - - # if field is not a number and has double quotes remove them, and always add - if [[ ! ${field} =~ ^[0-9]+$ ]]; then - # replacement for key with nested object - field=${field/./\",\"} - field="$(_trimDoubleQuotes "${field}")" - field="\"${field}\"" - _ovhapilib_echo_debug "field: ${field}" - - fi - - result=$(echo "${json}" | "${OVHAPI_BASHCLIENT_LIB}/JSON.sh" -l | grep -F "[${field}]" | sed -r "s/\[${field}\]\s+(.*)/\1/") - - # when result is between double quotes, remove first and last - result=$(_trimDoubleQuotes "${result}") - if [ -n "${result}" ]; then - echo "${result}" - fi -} - -# usage : getJSONValues "json" -# -# if key is a number, return only values -# if key is a string, return pair of key:value per line -getJSONValues() -{ - local json="$1" - local json_keys= - local json_key= - local json_value= - - json_keys=$(getJSONKeys "${json}") - - for json_key in ${json_keys} - do - json_value=$(getJSONValue "${json}" "${json_key}") - # if key is a number, only value is wanted - if [[ ${json_key} =~ ^[0-9]+$ ]]; then - echo "${json_value}" - else - # key is a field, show with value - echo "${json_key}:${json_value}" - fi - done -}