Skip to content

Tab completion for buf curl #2044

@torkelrogstad

Description

@torkelrogstad

Thanks for creating buf curl! I think it's a very promising tool that will make it a lot easier to work with gRPC and Connect APIs.

An "obvious" and killer feature that's missing from buf curl is tab completion of services and methods. It would make for a fantastic developer experience if buf curl https://my-api.com/<TAB> could list out the available endpoints and methods.

A few ideas for how this could be done:

  1. Check and see if we're in a folder (or sub-folder) that contains a Buf module or workspace. In that case the module could be built, and the endpoints and methods enumerated.
  2. Attempt to do reflection on the written URL (and with any header flags, if passed into the command).
  3. If the --schema flag is passed, inspect that to find the data.

I don't know how buf curl is typically used. But for my personal usage, I'm mostly using it against local services I'm actively working on, for manual testing and debugging. Prior to buf curl being developed, I've used a Fish script called bufcurl that wraps grpcurl and uses the approach I described in point 1. I've implemented tab completion for that (it only lists services, I haven't gotten around to implementing the methods). Pasting this below, for inspiration/context on what I'm thinking.

A final note: I'm configuring my bufcurl script through environment variables. In the folder containing services I'm working on I typically set these environment variables using an environment file. I think this is crucial to providing the best possible DX, as it simplifies the command needed to be invoked.

The script itself
function bufcurl
    if test -n "$BUFCURL_PLAINTEXT" 
        set plaintext_arg '-plaintext'
    end

    # Check if we get passed a data arg, with
    # a trailing `=`
    if string match -q -- "*-d=*" "$argv"
        set idx (contains --index -- -d= $argv)
        set data_args $argv[$idx] 

        # only do it once, because it's the same arg 
        set --erase argv[$idx]

    # Check if we get a data arg, without 
    # trailing `=`
    else if contains -- -d $argv
        set idx (contains --index -- -d $argv)
        set data_args $argv[$idx] $argv[(math "$idx + 1")]

        # do it twice, both flag and value
        set --erase argv[$idx]
        set --erase argv[$idx]

    end

    if test -n "$BUFCURL_HEADER" 
        set header_arg -H="$BUFCURL_HEADER"
    end

    if test -n "$BUFCURL_VERBOSE"
        set verbose_arg "-v"
    end

    # If we get passed a target, this overwrites 
    # env var target. 
    if string match -q -- "*localhost*" "$argv" 
        set --erase BUFCURL_TARGET
    end

    set working_dir (pwd)
    while not test -e buf.yaml -o -e buf.yml -o -e buf.work.yaml -o -e buf.work.yml 
        if test (pwd) = "/"
            echo could not find Buf config! >&2
            cd $working_dir
            return 1
        end

        __bufcurl_log pwd=(pwd), going backwards
        
        cd ..
    end

    __bufcurl_log found buf_dir=(pwd)
    set buf_dir (pwd)
    cd $working_dir

    __bufcurl_log plaintext: $plaintext_arg
    __bufcurl_log target: $BUFCURL_TARGET
    grpcurl $verbose_arg $plaintext_arg $header_arg $data_args -protoset (buf build $buf_dir -o - | psub) $BUFCURL_TARGET $argv
end
The tab completions
$ cat $HOME/.config/fish/completions/bufcurl.fish
function __bufcurl_list_services
    buf build --exclude-source-info --output -#format=json | \
        jq --raw-output --compact-output \
        '.file | map ({service: .service, package: .package} | select(.service) | {service: .service[0].name, package: .package} | .package +"."+ .service  ) | .[]'
end

# Not implemented...
function __bufcurl_list_methods --argument-names service

end

function __bufcurl_complete
    # no buf setup!
    if not test -e buf.yaml && not test -e buf.work.yaml
        return 
    end

    set -l cmd (commandline -poc)
    set -e cmd[1]

    if not __fish_seen_subcommand_from (__bufcurl_list_services)
        __bufcurl_list_services
        return 
    end

    __bufcurl_list_methods



 
end

complete -c bufcurl --no-files -a "(__bufcurl_complete)"

Metadata

Metadata

Assignees

No one assigned

    Labels

    FeatureNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions