From f40fc3a025fa9267c9d8491b660b89ff2b9b1aaa Mon Sep 17 00:00:00 2001 From: Didier BONNEFOI Date: Fri, 30 Jun 2017 18:57:05 +0200 Subject: [PATCH 1/4] add wrapper for using ovh-api-bash-client with external scripts --- README.md | 41 +++++++++++++++ ovh-api-lib.sh | 114 ++++++++++++++++++++++++++++++++++++++++ samples/list-domains.sh | 20 +++++++ 3 files changed, 175 insertions(+) create mode 100644 ovh-api-lib.sh create mode 100755 samples/list-domains.sh diff --git a/README.md b/README.md index ad3d4a2..e5410e3 100644 --- a/README.md +++ b/README.md @@ -74,3 +74,44 @@ To create a Consumer key for different account or usage (profile is created if m ./ovh-api-bash-client.sh --profile demo1 --init ./ovh-api-bash-client.sh --profile demo2 --init ``` + + +Embedded lib for external scripts +---------- + +### ovh-api-lib.sh + +#### OvhRequestApi + +- OvhRequestApi() : wrapper to ovh-api-bash-client.sh + +``` + OvhRequestApi url [method] [post_data] +``` + +return values in OVHAPI_HTTP_STATUS and OVHAPI_HTTP_RESPONSE + + +#### JSon stuff +- getJSONString() : unquote string value +- getJSONValue() : get JSON value as is +- getJSONValues() : get all JSON values at once +- getJSONArrayLength() : count array elements + +### sample usage + +Once you've an available OVH API authentication, you can use the library : + +To set a profile, define value OVHAPI_BASHCLIENT_PROFILE (can be used inside or outside your script) + +**sample-script.sh** + +``` + source path/to/ovh-api-bash-client/ovh-api-lib.sh || exit 1 + OvhRequestApi /me +``` + +**sample** + + +OVHAPI_BASHCLIENT_PROFILE=demo samples/list-domains.sh diff --git a/ovh-api-lib.sh b/ovh-api-lib.sh new file mode 100644 index 0000000..3ec1ab4 --- /dev/null +++ b/ovh-api-lib.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash + + +readonly OVHAPI_BASHCLIENT_DIR=$(dirname "${BASH_SOURCE[0]}") +readonly OVHAPI_BASHCLIENT_BIN="${OVHAPI_BASHCLIENT_DIR}/ovh-api-bash-client.sh" +readonly OVHAPI_BASHCLIENT_LIB="${OVHAPI_BASHCLIENT_DIR}/libs" + +OVHAPI_HTTP_STATUS= +OVHAPI_HTTP_RESPONSE= + +# ensure the client is available +if [ ! -f "${OVHAPI_BASHCLIENT_BIN}" ]; then + echo "${OVHAPI_BASHCLIENT_BIN} not found" + exit 1 +fi + +# to set a profile, define value in the variable OVHAPI_BASHCLIENT_PROFILE +# 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 quote= + + local cmd=(${OVHAPI_BASHCLIENT_BIN}) + + 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 [ "${method}" == "POST" ]; then + local cmdfile=$(mktemp "/tmp/OvhRequestApi.postcmd.sh.XXXXXX") + # best way found to correctly pass quoted argument to a command called via a function + + # if json content has single or double quotes inside, to use the opposite + if echo ${data} | grep -q '"'; then + quote=\' + elif echo ${data} | grep -q "'"; then + quote=\" + fi + + cmd+=(--data ${quote}${data}${quote}) + # inject all command to a temp file, execute and drop the file + echo ${cmd[@]} > ${cmdfile} + client_response=$(bash ${cmdfile}) + rm ${cmdfile} + else + client_response=$(${cmd[@]}) + fi + + OVHAPI_HTTP_STATUS=$(echo ${client_response} | cut -d ' ' -f1) + OVHAPI_HTTP_RESPONSE="$(echo ${client_response} | cut -d ' ' -f2-)" + + # debug information, go to stderr + echo http_status=${OVHAPI_HTTP_STATUS} >&2 + +} + +## vendors's JSON parsing + +# usage : getJSONString "json" field +# remove quotes (first and last character) from wanted field +getJSONString() +{ + local json="$1" + local field="$2" + local result=$(getJSONValue "${json}" "${field}") + echo ${result:1:-1} +} + +# usage : getJSONValue "json" field +getJSONValue() +{ + local json="$1" + local field="$2" + echo ${json} | ${OVHAPI_BASHCLIENT_LIB}/JSON.sh -l | grep "\[${field}\]" | sed -r "s/\[${field}\]\s+(.*)/\1/" +} + +# usage : getJSONValues "json" +# return one value per line +getJSONValues() +{ + local json="$1" + local i=0 + local length=$(getJSONArrayLength "${json}") + + while [ $i -lt ${length} ]; + do + getJSONValue "$json" $i + let i+=1 + done + +} + +# usage : getJSONArrayLength "json" +# return the size of json array +getJSONArrayLength() +{ + local json="$1" + echo ${json} | ${OVHAPI_BASHCLIENT_LIB}/JSON.sh -l | wc -l +} diff --git a/samples/list-domains.sh b/samples/list-domains.sh new file mode 100755 index 0000000..00d6f98 --- /dev/null +++ b/samples/list-domains.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +HERE=$(dirname "${BASH_SOURCE[0]}") +source ${HERE}/../ovh-api-lib.sh || exit 1 + +OvhRequestApi "/me" +if [ "${OVHAPI_HTTP_STATUS}" != "200" ]; then + echo "profile error:" + echo ${OVHAPI_HTTP_RESPONSE} + exit +fi + +OvhRequestApi "/domain" + +if [ ${OVHAPI_HTTP_STATUS} -eq 200 ]; then + domains=$(getJSONValues "${OVHAPI_HTTP_RESPONSE}") + for domain in ${domains} + do + echo "- ${domain}" + done +fi From af4a56da66e039212cee6bb671ec6a109b2e7e6a Mon Sep 17 00:00:00 2001 From: Didier BONNEFOI Date: Mon, 3 Jul 2017 00:38:10 +0200 Subject: [PATCH 2/4] OVH API Lib enhancements: OvhRequestApi() review: - ovh-api-bash-client.sh commandline piped to bash, for correct quotes management - add target support with OVHAPI_TARGET variable Wrappers for JSON.sh enhancements: - JSON nested object managed, key displayed/set with a dot as separator. - getJSONValues() support for object and nested object - getJSONValue() hash support (getJSONString() removed) - remove double quotes from field for getJSONKeys() and getJSONValue() - getJSONArrayLength() removed, result can be calculated from getJSONValues() result Lib debugging: - add _ovhapilib_echo_debug() : redirect arguments to stderr - add OVHAPILIB_DEBUG variable --- README.md | 28 +++--- ovh-api-lib.sh | 156 ++++++++++++++++++++---------- samples/list-domains.sh | 24 +++-- samples/my-informations.sh | 18 ++++ samples/post-domain-TXT-record.sh | 42 ++++++++ 5 files changed, 199 insertions(+), 69 deletions(-) create mode 100755 samples/my-informations.sh create mode 100755 samples/post-domain-TXT-record.sh diff --git a/README.md b/README.md index e5410e3..55c6e79 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Embedded lib for external scripts #### OvhRequestApi -- OvhRequestApi() : wrapper to ovh-api-bash-client.sh +OvhRequestApi() is wrapper to ovh-api-bash-client.sh ``` OvhRequestApi url [method] [post_data] @@ -92,26 +92,26 @@ Embedded lib for external scripts return values in OVHAPI_HTTP_STATUS and OVHAPI_HTTP_RESPONSE -#### JSon stuff -- getJSONString() : unquote string value -- getJSONValue() : get JSON value as is +#### 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 -- getJSONArrayLength() : count array elements ### sample usage Once you've an available OVH API authentication, you can use the library : -To set a profile, define value OVHAPI_BASHCLIENT_PROFILE (can be used inside or outside your script) +- 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 -**sample-script.sh** +This variables can be set in your script or exported from commandline + +You can find some samples scripts in the **samples/** directory + +**sample usage** ``` - source path/to/ovh-api-bash-client/ovh-api-lib.sh || exit 1 - OvhRequestApi /me + OVHAPI_BASHCLIENT_PROFILE=demo samples/list-domains.sh ``` - -**sample** - - -OVHAPI_BASHCLIENT_PROFILE=demo samples/list-domains.sh diff --git a/ovh-api-lib.sh b/ovh-api-lib.sh index 3ec1ab4..3ae172e 100644 --- a/ovh-api-lib.sh +++ b/ovh-api-lib.sh @@ -8,13 +8,41 @@ readonly OVHAPI_BASHCLIENT_LIB="${OVHAPI_BASHCLIENT_DIR}/libs" OVHAPI_HTTP_STATUS= OVHAPI_HTTP_RESPONSE= +# debug output: should be setted to 1 from external script +OVHAPILIB_DEBUG=${OVHAPILIB_DEBUG:-0} + +# use ovh-api-bash-client default target if not set +OVHAPI_TARGET=${OVHAPI_TARGET:-} + # ensure the client is available if [ ! -f "${OVHAPI_BASHCLIENT_BIN}" ]; then echo "${OVHAPI_BASHCLIENT_BIN} not found" exit 1 fi -# to set a profile, define value in the variable OVHAPI_BASHCLIENT_PROFILE +# debug output if wanted +_ovhapilib_echo_debug() +{ + if [ "${OVHAPILIB_DEBUG}" == "1" ]; then + echo "[debug:${FUNCNAME[1]}] $*" >&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 @@ -25,90 +53,120 @@ OvhRequestApi() local method=$2 local data=$3 - local client_response= - local quote= 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 [ "${method}" == "POST" ]; then - local cmdfile=$(mktemp "/tmp/OvhRequestApi.postcmd.sh.XXXXXX") - # best way found to correctly pass quoted argument to a command called via a function - - # if json content has single or double quotes inside, to use the opposite - if echo ${data} | grep -q '"'; then - quote=\' - elif echo ${data} | grep -q "'"; then - quote=\" - fi - - cmd+=(--data ${quote}${data}${quote}) - # inject all command to a temp file, execute and drop the file - echo ${cmd[@]} > ${cmdfile} - client_response=$(bash ${cmdfile}) - rm ${cmdfile} - else - client_response=$(${cmd[@]}) + if [ -n "${OVHAPI_TARGET}" ]; then + cmd+=(--target ${OVHAPI_TARGET}) fi - OVHAPI_HTTP_STATUS=$(echo ${client_response} | cut -d ' ' -f1) - OVHAPI_HTTP_RESPONSE="$(echo ${client_response} | cut -d ' ' -f2-)" + if [ "${method}" == "POST" ]; then + # double-quote data content for bash input + data=$(printf "%q" "${data}") + cmd+=(--data ${data}) + fi - # debug information, go to stderr - echo http_status=${OVHAPI_HTTP_STATUS} >&2 + _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}" } -## vendors's JSON parsing -# usage : getJSONString "json" field -# remove quotes (first and last character) from wanted field -getJSONString() + +## vendor's JSON.sh parsing functions + +# usage : getJSONKeys "json" +# +# return JSON keys list without double quote if present +getJSONKeys() { - local json="$1" - local field="$2" - local result=$(getJSONValue "${json}" "${field}") - echo ${result:1:-1} + 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" - echo ${json} | ${OVHAPI_BASHCLIENT_LIB}/JSON.sh -l | grep "\[${field}\]" | sed -r "s/\[${field}\]\s+(.*)/\1/" + 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" -# return one value per line +# +# 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 i=0 - local length=$(getJSONArrayLength "${json}") + local json_keys= + local json_key= + local json_value= - while [ $i -lt ${length} ]; + json_keys=$(getJSONKeys "${json}") + + for json_key in ${json_keys} do - getJSONValue "$json" $i - let i+=1 + 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 - -} - -# usage : getJSONArrayLength "json" -# return the size of json array -getJSONArrayLength() -{ - local json="$1" - echo ${json} | ${OVHAPI_BASHCLIENT_LIB}/JSON.sh -l | wc -l } diff --git a/samples/list-domains.sh b/samples/list-domains.sh index 00d6f98..e9977d8 100755 --- a/samples/list-domains.sh +++ b/samples/list-domains.sh @@ -1,20 +1,32 @@ #!/usr/bin/env bash -HERE=$(dirname "${BASH_SOURCE[0]}") +HERE=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) source ${HERE}/../ovh-api-lib.sh || exit 1 OvhRequestApi "/me" + if [ "${OVHAPI_HTTP_STATUS}" != "200" ]; then echo "profile error:" - echo ${OVHAPI_HTTP_RESPONSE} + echo "${OVHAPI_HTTP_RESPONSE}" exit fi OvhRequestApi "/domain" -if [ ${OVHAPI_HTTP_STATUS} -eq 200 ]; then - domains=$(getJSONValues "${OVHAPI_HTTP_RESPONSE}") - for domain in ${domains} +if [ "${OVHAPI_HTTP_STATUS}" -eq 200 ]; then + domains=($(getJSONValues "${OVHAPI_HTTP_RESPONSE}")) + echo "number of domains=${#domains[@]}" + + # for example, only list for first domain + #for domain in "${domains[@]}" + for domain in "${domains[0]}" do - echo "- ${domain}" + echo -e "\n== informations about ${domain} ==" + OvhRequestApi "/domain/${domain}" + echo "-- single value --" + # key can be passed with/without double quote + getJSONValue "${OVHAPI_HTTP_RESPONSE}" lastUpdate + getJSONValue "${OVHAPI_HTTP_RESPONSE}" '"transferLockStatus"' + echo "-- get all values --" + getJSONValues "${OVHAPI_HTTP_RESPONSE}" done fi diff --git a/samples/my-informations.sh b/samples/my-informations.sh new file mode 100755 index 0000000..9e6d46b --- /dev/null +++ b/samples/my-informations.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +HERE=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) +source ${HERE}/../ovh-api-lib.sh || exit 1 + +OvhRequestApi /me + +if [ "${OVHAPI_HTTP_STATUS}" != "200" ]; then + echo "profile error:" + echo "${OVHAPI_HTTP_RESPONSE}" + exit +else + echo "-- all fields --" + getJSONValues "${OVHAPI_HTTP_RESPONSE}" + echo "-- only some fields --" + getJSONValue "${OVHAPI_HTTP_RESPONSE}" "email" + getJSONValue "${OVHAPI_HTTP_RESPONSE}" "currency.code" + getJSONValue "${OVHAPI_HTTP_RESPONSE}" "currency.symbol" +fi diff --git a/samples/post-domain-TXT-record.sh b/samples/post-domain-TXT-record.sh new file mode 100755 index 0000000..e58ae24 --- /dev/null +++ b/samples/post-domain-TXT-record.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +HERE=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) +source ${HERE}/../ovh-api-lib.sh || exit 1 + +OvhRequestApi "/me" + +if [ "${OVHAPI_HTTP_STATUS}" != "200" ]; then + echo "profile error:" + echo "${OVHAPI_HTTP_RESPONSE}" + exit +fi + +if [ -z "${OVH_DOMAIN}" ]; then + echo -e "please set one of your domains with :\nOVH_DOMAIN=your_domain.tld" + echo -e "choose in :\n" + + OvhRequestApi "/domain" + getJSONValues "${OVHAPI_HTTP_RESPONSE}" + exit 1 +fi + +txt_field="ovhapilib" + +txt_value="test1: text with space and quo't'es" + +# avoid backslashes :-) : +CUSTOMDATA=$(cat < Date: Fri, 14 Jul 2017 01:09:28 +0200 Subject: [PATCH 3/4] JSON functions improvements - single execution of JSON.sh, now read result from a cache - JSON wrapper now use AWK for best performances, even on big JSON source - remove JSON string argument from getJSON*() functions - move JSON functions to a standalone file - add JSONSHLIB_DEBUG variable - add JSONSH_DIR to override JSON.sh path - update samples scripts - rework file tree - update README - update .gitignore --- .gitignore | 2 +- README.md | 40 +--- contrib/README.md | 104 +++++++++ contrib/jsonsh-lib.sh | 212 ++++++++++++++++++ contrib/ovh-api-lib.sh | 86 +++++++ {samples => contrib/samples}/list-domains.sh | 10 +- .../samples}/my-informations.sh | 8 +- .../samples}/post-domain-TXT-record.sh | 8 +- contrib/test/jsonshlib_test.sh | 94 ++++++++ ovh-api-lib.sh | 172 -------------- 10 files changed, 512 insertions(+), 224 deletions(-) create mode 100644 contrib/README.md create mode 100644 contrib/jsonsh-lib.sh create mode 100644 contrib/ovh-api-lib.sh rename {samples => contrib/samples}/list-domains.sh (73%) rename {samples => contrib/samples}/my-informations.sh (60%) rename {samples => contrib/samples}/post-domain-TXT-record.sh (87%) create mode 100755 contrib/test/jsonshlib_test.sh delete mode 100644 ovh-api-lib.sh 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 -} From ae357400574f19300c914a431a6433ee04c00a38 Mon Sep 17 00:00:00 2001 From: Didier BONNEFOI Date: Wed, 19 Jul 2017 11:45:54 +0200 Subject: [PATCH 4/4] OvhRequestApi() enhancements - OVHAPI_HTTP_STATUS always return a numeric value - raise error 500 with help message if OVHAPI_HTTP_RESPONSE has many lines - update samples scripts with OvhRequestApi() --- contrib/jsonsh-lib.sh | 9 +++------ contrib/ovh-api-lib.sh | 14 ++++++++++++-- contrib/samples/list-domains.sh | 6 ++++-- contrib/samples/my-informations.sh | 18 +++++++++--------- contrib/samples/post-domain-TXT-record.sh | 4 ++-- 5 files changed, 30 insertions(+), 21 deletions(-) diff --git a/contrib/jsonsh-lib.sh b/contrib/jsonsh-lib.sh index 028f916..755d651 100644 --- a/contrib/jsonsh-lib.sh +++ b/contrib/jsonsh-lib.sh @@ -161,7 +161,7 @@ _JSonSH_rewrite_output() } # -# return JSON keys +# print JSON keys # # usage : getJSONKeys # @@ -176,12 +176,9 @@ getJSONKeys() } # -# return the value for a defined filed +# print the value for a defined field # -# if field is a string, it can be set with/without double quotes -# if the result is between double quotes, only get the value inside -# -# usage : getJSONValue "json" field +# usage : getJSONValue field # getJSONValue() { diff --git a/contrib/ovh-api-lib.sh b/contrib/ovh-api-lib.sh index f37ca09..539f28a 100644 --- a/contrib/ovh-api-lib.sh +++ b/contrib/ovh-api-lib.sh @@ -4,7 +4,8 @@ readonly OVHAPI_BASHCLIENT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}")/.." && pwd) readonly OVHAPI_BASHCLIENT_BIN="${OVHAPI_BASHCLIENT_DIR}/ovh-api-bash-client.sh" readonly OVHAPI_BASHCLIENT_CONTRIB_DIR="${OVHAPI_BASHCLIENT_DIR}/contrib" -. "${OVHAPI_BASHCLIENT_DIR}/contrib/jsonsh-lib.sh" || exit 1 +JSONSH_DIR="${OVHAPI_BASHCLIENT_DIR}/libs/" +. "${OVHAPI_BASHCLIENT_CONTRIB_DIR}/jsonsh-lib.sh" || exit 1 OVHAPI_HTTP_STATUS= OVHAPI_HTTP_RESPONSE= @@ -43,13 +44,14 @@ OvhRequestApi() local data=$3 local client_response= - + local cmd_profile= local cmd=(${OVHAPI_BASHCLIENT_BIN}) ## construct arguments array if [ -n "${OVHAPI_BASHCLIENT_PROFILE}" ]; then cmd+=(--profile ${OVHAPI_BASHCLIENT_PROFILE}) fi + cmd_profile=${cmd[*]} if [ -n "${url}" ]; then cmd+=(--url ${url}) @@ -77,6 +79,14 @@ OvhRequestApi() OVHAPI_HTTP_STATUS=$(echo "${client_response}" | cut -d ' ' -f1) OVHAPI_HTTP_RESPONSE="$(echo "${client_response}" | cut -d ' ' -f2-)" + # catch profile error + if [[ ! ${OVHAPI_HTTP_STATUS} =~ ^[0-9]+$ ]] && [[ ${OVHAPI_HTTP_RESPONSE} == *$'\n'* ]]; then + OVHAPI_HTTP_STATUS=500 + OVHAPI_HTTP_RESPONSE=$(cat <