Skip to content

Commit

Permalink
Merge pull request #32 from davide-scola/0.x-add_grenache_request
Browse files Browse the repository at this point in the history
[0.x] Request
  • Loading branch information
davide-scola authored Jan 23, 2025
2 parents 3fec502 + a16573b commit 6a9c966
Show file tree
Hide file tree
Showing 6 changed files with 281 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ grenache-get
grenache-put
grenache-keygen
grenache-lookup
grenache-request
grenache-announce

#
Expand Down
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* [Retreive items from the DHT](#retreive-items-from-the-dht)
* [Lookup peers](#lookup-peers)
* [Announce services](#announce-services)
* [Query services](#query-services)
* [Maintainers](#maintainers)


Expand Down Expand Up @@ -184,6 +185,55 @@ grenache-announce --help
to retrieve the complete options list.


## Query services

The `grenache-request` command sends a request to a service in the DHT. To query the [rest:ext:helpdesk:foo](https://github.com/bitfinexcom/bfx-ext-helpdesk-js) service, simply run something like this:

```bash
grenache-request 'rest:ext:helpdesk:foo' 'getDepartments'
```

Action arguments are optional and can be omitted altogether. However, to pass an argument to an action, use the `-a` switch or its long form `--arg`; to query the [rest:net:util](https://github.com/bitfinexcom/bfx-util-net-js) service in order to get information about IP _208.67.222.222_, simply run something like this:

```bash
grenache-request -a '"208.67.222.222"' 'rest:util:net' 'getIpInfo'
```

To send multiple arguments, simply repeat the `-a` switch or its long form `--arg` as many times as needed; arguments will be passed to the service action in the same order as they were supplied on the command line. To query the [rest:ext:gpg](https://github.com/bitfinexcom/bfx-ext-gpg-js) service in order to get a signature for the _hello_ message, simply run something like this:

```bash
grenache-request -a '"68656c6c6f"' -a '{"userId":1}' 'rest:ext:gpg' 'getDigitalSignature'
```

Arguments are treated as _JSON_-encoded text; sometimes, when dealing with plain strings, this can be somewhat cumbersome. In such cases, the `-s` switch or its long form `--string` can be used to pass a string as-is or `-n` or its long form `--numeric` when accepting an option that is intended to be treated as a numeric value.

Typically, the output is a _JSON_-encoded text. Sometimes, filtering this text can be useful; in such cases, output can be selected using the `-q` switch or its long form `--query`, as shown below:

```bash
grenache-request -q 'map(select(.active_members > 3))' 'rest:ext:helpdesk:bar' 'getTeams'
```

The query is passed as-is to [jq](https://jqlang.github.io/jq/) and will be applied directly to the third position of the response array; refer to the official [filter documentation](https://jqlang.github.io/jq/manual/#basic-filters) for more details on the syntax to be used. However, there are cases where a plain string written directly to the standard output is preferable rather than formatted as a _JSON_ string with quotes, perhaps when pipe the output to another command; in such cases, the `-r` switch or its long form `--raw` can be used, as shown below:

```bash
grenache-request -q '.[1].timezone' -r -s '208.67.222.222' 'rest:util:net' 'getIpGeo'
```

Under normal circumstances, workflow is to look up the service via [grenache-lookup](#lookup-peers) and then send the request to the worker. There might be cases, perhaps when dealing with a system without a running [grape](https://github.com/bitfinexcom/grenache-grape), where it would be useful to query a service without looking it up first; in such cases, the `-w` switch or its long form `--worker` can be used, as shown below:

```bash
grenache-request -r -s '776f726c64' -a '{"userId":8}' -w '10.0.0.1:1337' 'rest:ext:gpg' 'getDigitalSignature'
```

Those are the main options; see

```bash
grenache-request --help
```

to retrieve the complete options list.


## Maintainers

Current maintainers:
Expand Down
5 changes: 3 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ dnl implied. See the License for the specific language governing permissions
dnl and limitations under the License.

AC_PREREQ([2.69])
AC_INIT([grenache-cli],[0.7.1],[davide@bitfinex.com])
AC_INIT([grenache-cli],[0.8.0],[davide@bitfinex.com])

AC_SUBST([VERSION], [0.7.1])
AC_SUBST([VERSION], [0.8.0])
AC_SUBST([SB], [`$srcdir/shtool echo -n -e %B`])
AC_SUBST([EB], [`$srcdir/shtool echo -n -e %b`])

Expand Down Expand Up @@ -190,6 +190,7 @@ AC_CONFIG_FILES([ \
src/grenache-get \
src/grenache-keygen \
src/grenache-lookup \
src/grenache-request \
src/grenache-announce \
tests/Makefile \
tests/sign-test-vector-1.sh \
Expand Down
1 change: 1 addition & 0 deletions doc/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ grenache-get.1 \
grenache-put.1 \
grenache-keygen.1 \
grenache-lookup.1 \
grenache-request.1 \
grenache-announce.1

MAINTAINERCLEANFILES = \
Expand Down
1 change: 1 addition & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ grenache-put \
grenache-get \
grenache-keygen \
grenache-lookup \
grenache-request \
grenache-announce

noinst_LTLIBRARIES = \
Expand Down
225 changes: 225 additions & 0 deletions src/grenache-request.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
#!/bin/bash
############################################################################
# This file is part of Grenache Command Line Interface. #
# #
# Copyright (C) 2017-2025 Davide Scola <davide@bitfinex.com> #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
# not use this file except in compliance with the License. You may obtain #
# a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or #
# implied. See the License for the specific language governing permissions #
# and limitations under the License. #
############################################################################

readonly ME="${BASH_SOURCE[0]}"
readonly WHOAMI="$(@READLINK@ -snf "${ME}")"
readonly ARGV=$(@GETOPT@ -o 'a:cg:hn:p:q:rs:tVw:' --long 'arg:,color,grape:,help,numeric:,port:,query:,raw,string:,tls,version,worker:' -n "${ME##*/}" -- "$@") || exit 1

TLS=''
COLOR=''
EXIT_CODE=1
REQUEST_ARGC=0
RESPONSE_ARGV=()
REQUEST_WORKER=''
REQUEST_QUERY='.'
RESPONSE_QUERY='.'
REQUEST_ARGUMENTS='[]'
LOOKUP_ARGV=(--random)
REQUEST_ARGV=(--compact-output --monochrome-output)


# Show program's help.
function show_help {
@CAT@ <<_EOF
${ME##*/} sends a request to a service in the DHT
Usage: ${ME##*/} [OPTION]... SERVICE ACTION
Options:
-a, --arg Add an argument, JSON encoded
-c, --color Pretty print
-g, --grape Set the Grape hostname
-n, --numeric Add a numeric argument
-p, --port Set the Grape port number
-q, --query Filter response against query
-r, --raw Print string output as-is
-s, --string Add a string argument
-t, --tls Enable TLS
-w, --worker Don't lookup, use this worker
-h, --help Show help message
-V, --version Show version information
The \`--arg', \`--numeric' and \`--string' options can be repeated several times
to add multiple arguments, which will be passed in the same order as they were
supplied on the command line.
Examples:
${ME##*/} 'foo' 'bar' Call the bar action of the foo service
${ME##*/} -n 1 'foo' 'bar' The same thing, but passing a numeric argument
Report bugs to <@PACKAGE_BUGREPORT@>.
_EOF

exit 1
}

# Show program's version.
function show_version {
@CAT@ <<_EOF
${WHOAMI##*/} @PACKAGE_VERSION@
Copyright (C) 2017-2025 Davide Scola <@PACKAGE_BUGREPORT@>
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
Written by Davide Scola.
_EOF

exit 1
}


while true; do
case "$1" in
-a | --arg )
REQUEST_ARGV+=(--arg "a$((++REQUEST_ARGC))" "${2}")
REQUEST_QUERY+=' += [ $a'"${REQUEST_ARGC}"' | fromjson ] | .'; shift 2
;;
-c | --color )
COLOR='yes'; shift 1
;;
-g | --grape )
[[ -z "${2}" ]] && {
echo "${ME##*/}: error: empty Grape hostname." >&2
exit 1
}

LOOKUP_ARGV+=(--grape "${2}"); shift 2
;;
-h | --help )
show_help; shift 1
;;
-n | --numeric )
REQUEST_ARGV+=(--arg "n$((++REQUEST_ARGC))" "${2}")
REQUEST_QUERY+=' += [ $n'"${REQUEST_ARGC}"' | tonumber ] | .'; shift 2
;;
-p | --port )
[[ -z "${2}" ]] && {
echo "${ME##*/}: error: empty Grape port." >&2
exit 1
}

LOOKUP_ARGV+=(--port "${2}"); shift 2
;;
-q | --query )
[[ -z "${2}" ]] && {
echo "${ME##*/}: error: empty response query." >&2
exit 1
}

RESPONSE_QUERY="${2}"; shift 2
;;
-r | --raw )
RESPONSE_ARGV+=(--raw-output); shift 1
;;
-s | --string )
REQUEST_ARGV+=(--arg "s$((++REQUEST_ARGC))" "${2}")
REQUEST_QUERY+=' += [ $s'"${REQUEST_ARGC}"' | tostring ] | .'; shift 2
;;
-t | --tls )
TLS='yes'; LOOKUP_ARGV+=(--tls); shift 1
;;
-V | --version )
show_version; shift 1
;;
-w | --worker )
[[ -z "${2}" ]] && {
echo "${ME##*/}: error: empty worker address." >&2
exit 1
}

[[ "${2##*:}" =~ ^[0-9]+$ ]] || {
echo "${ME##*/}: error: invalid worker port number." >&2
exit 1
}

[[ "$((${2##*:} & 0xFFFF))" -eq 0 ]] && {
echo "${ME##*/}: error: worker port number must be greater than zero." >&2
exit 1
}

[[ "${2##*:}" -ne "$((${2##*:} & 0xFFFF))" ]] && {
echo "${ME##*/}: error: worker port number too big." >&2
exit 1
}

REQUEST_WORKER="${2%:*}:$((${2##*:} & 0xFFFF))"; shift 2
;;
-- ) shift; break ;;
* ) break ;;
esac
done

[[ -z "${COLOR}" ]] && {
RESPONSE_ARGV+=(--compact-output --monochrome-output)
}

[[ "${#}" -lt 2 ]] && {
show_help
}

[[ -f "${HOME}/.grenache-cli/grenache-cli.conf" ]] || {
echo "${ME##*/}: error: you need to run \`grenache-keygen' first." >&2
exit "${EXIT_CODE}"
}

exec {stderr}>&2

[[ x"${GRENACHE_CLI_DEBUG:+set}" != xset ]] && {
exec 2>/dev/null
}

[[ "${REQUEST_ARGC}" -gt 0 ]] && {
REQUEST_ARGUMENTS="$(@JQ@ "${REQUEST_ARGV[@]}" --from-file <(echo "${REQUEST_QUERY}") <<<"${REQUEST_ARGUMENTS}")"

[[ "${?}" -ne 0 ]] && {
echo "${ME##*/}: error: provided arguments are invalid." >&"${stderr}"

exec {stderr}>&-
exit "${EXIT_CODE}"
}
}

JSON="$(@CURL@ -qK "${HOME}/.grenache-cli/grenache-cli.conf" -A '@PACKAGE@/@PACKAGE_VERSION@' "http${TLS:+s}://${REQUEST_WORKER:-$(grenache-lookup "${LOOKUP_ARGV[@]}" "${1}")}" < <( \
@JQ@ --null-input --compact-output --monochrome-output \
--arg 'action' "${2}" \
--arg 'service' "${1}" \
--arg 'rid' "$(@UUIDGEN@)" \
--arg 'args' "${REQUEST_ARGUMENTS}" \
'[ $rid, $service, { "action": $action, "args": $args | fromjson } ]' \
))"

[[ -z "${JSON}" ]] && {
echo "${ME##*/}: error: service ${1} cannot be queried." >&"${stderr}"

exec {stderr}>&-
exit "${EXIT_CODE}"
}

@JQ@ --exit-status '.[1]' >/dev/null <<<"${JSON}" && {
@JQ@ --raw-output '.[1]' <<<"${JSON}" >&"${stderr}"
} || {
EXIT_CODE=0
@JQ@ "${RESPONSE_ARGV[@]}" --from-file <(printf '.[2] | %s' "${RESPONSE_QUERY}") <<<"${JSON}"
}

exec {stderr}>&-
exit "${EXIT_CODE}"

0 comments on commit 6a9c966

Please sign in to comment.