Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More cli support #18

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions HACKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,7 @@ Some rules support multiple languages, like `____func_code____`, which generates
A relatively easy way to add a feature in Cognac is to start by modifying the generated code (such as "osc-sdk.c", "main.c", etc.). First, make your changes directly in the generated code, ensure it works as expected, and then modify the generator accordingly. This approach helps you know exactly what to look for in the generator.

For exemple, if you modify a function like `parse_thatarg()` in "osc_sdk.c", once the changes are working, you can then search (using a tool like grep) through the generator code to find where `parse_thatarg` is generated. From there, you can add modifications to the generator to make the change permanent and automated.

## helpers.sh

Most functions in helper.sh are documented, so read through it if you want to understand some of the most commonly used functions in cognac_gen.
22 changes: 10 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,35 +36,33 @@ bin/funclist: bin/funclist.c
bin/line_check: bin/line_check.c
$(CC) -O3 bin/line_check.c -o bin/line_check

main.c: bin/line_check osc-api.json call_list arguments-list.json config.sh main_tpl.c cognac_gen.sh mk_args.c.sh
main.c: bin/line_check osc-api.json call_list config.sh main_tpl.c cognac_gen.sh mk_args.c.sh
./cognac_gen.sh main_tpl.c main.c c

osc_sdk.c: bin/line_check osc-api.json call_list arguments-list.json config.sh lib.c cognac_gen.sh construct_data.c.sh mk_args.c.sh
osc_sdk.c: bin/line_check osc-api.json call_list config.sh lib.c cognac_gen.sh construct_data.c.sh mk_args.c.sh
./cognac_gen.sh lib.c osc_sdk.c c

osc_sdk.h: bin/line_check osc-api.json call_list arguments-list.json config.sh lib.h cognac_gen.sh mk_args.c.sh
osc_sdk.h: bin/line_check osc-api.json call_list config.sh lib.h cognac_gen.sh mk_args.c.sh
./cognac_gen.sh lib.h osc_sdk.h c

$(CLI_NAME)-completion.bash: bin/line_check osc-api.json call_list arguments-list.json config.sh oapi-cli-completion-tpl.bash cognac_gen.sh
$(CLI_NAME)-completion.bash: bin/line_check osc-api.json call_list config.sh oapi-cli-completion-tpl.bash cognac_gen.sh
./cognac_gen.sh oapi-cli-completion-tpl.bash $(CLI_NAME)-completion.bash bash

config.sh:
echo "alias json-search=$(JSON_SEARCH)" > config.sh
echo $(SED_ALIAS) >> config.sh
echo FUNCTION_SUFFIX=$(FUNCTION_SUFFIX) >> config.sh
echo "export CLI_NAME=$(CLI_NAME)" >> config.sh

arguments-list.json: osc-api.json
$(JSON_SEARCH) -s Request osc-api.json | $(JSON_SEARCH) -K properties \
| sed 's/]/ /g' \
| tr -d "\n[],\"" | sed -r 's/ +/ \n/g' \
| sort | uniq | tr -d "\n" > arguments-list.json
echo -e "debug()\n{" >> config.sh
echo -e '\tif [[ "$$DEBUG_MODE" == "1" ]] ; then echo "$$@" >&2 ; fi\n}' >> config.sh
echo export DEBUG_MODE=$(DEBUG_MODE) >> config.sh

call_list: osc-api.json bin/funclist
bin/funclist osc-api.json > call_list
bin/funclist osc-api.json $(FUNCLIST_ARG) > call_list


clean:
rm -vf osc-api.json call_list osc_sdk.c arguments-list.json osc_sdk.h main.c $(CLI_NAME) config.sh $(CLI_NAME)-completion.bash bin/line_check bin/funclist
rm -vf osc-api.json call_list osc_sdk.c osc_sdk.h main.c $(CLI_NAME) config.sh $(CLI_NAME)-completion.bash bin/line_check bin/funclist

.PHONY: clean list_api_version help make_cli

16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ The language argument is crucial as certain keywords might be interpreted differ
The script uses a file called osc-api.json, which represents the OpenAPI specification in JSON format.
For the Outscale API, the YAML source is converted to JSON using yq.

When generating API calls, COGNAC assumes that the OpenAPI file contains components named CallRequest.
When generating API calls, COGNAC by default assumes that the OpenAPI file contains components named CallRequest.
For example, if the API has a call named `CreatePony`, the corresponding component should be located at `#/components/schemas/CreatePonyRequest`.

You can change the suffix of functions using `./configure --function-suffix FUNCTION_SUFFIX`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can modify the suffix of functions by using the --function-suffix=FUNCTION_SUFFIX option with the ./configure command.


*Note: There are two versions of yq: one written in Python and one in Go. The default version depends on your distribution. On Arch-based distributions, the Python version is typically the default, whereas on Debian-based distributions, the Go version is default. COGNAC supports both, but to use the Go version, you need to pass `--yq-go` to `./configure`.*

### Example: Generating a CLI for a New API
Expand All @@ -44,22 +46,28 @@ To configure the Makefile to generate the CLI with the name `pony-cli`, and adju

Run the following command:
```bash
./configure --cli-name=pony-cli --api-script='curl -s https://ponyapi.yolo | yq $(YQ_ARG) | sed "s/Input/Request/" > osc-api.json'
./configure --cli-name=pony-cli --function-suffix Input --api-script='curl -s https://ponyapi.yolo | yq $(YQ_ARG)" > osc-api.json'
```

`-cli-name=pony-cli` set the generated binary name to `pony-cli`

```bash
--api-script='curl -s https://ponyapi.yolo | yq $(YQ_ARG) | sed "s/Input/Request/" > osc-api.json'
--function-suffix Input
```
Look for Function named XInput instead of XRequest.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Search for the function named XInput instead of XRequest.


```bash
--api-script='curl -s https://ponyapi.yolo | yq $(YQ_ARG) > osc-api.json'
```


This script is used to fetch the API file.


Here’s what the script does:

1. Retrieves the API in YAML format using curl -s `https://ponyapi.yolo/.`
2. Converts the YAML to JSON using yq `$(YQ_ARG)`. *Note the usage of `$(YQ_ARG)`, so ./configure can handle go version of yq*
3. Renames all components named `XInput` to `XRequest`.

Once this setup is complete, you can now use the Makefile. It's also a good idea to run ./configure --help, as it contains several useful options.
- `--wget-json-search`: Helps with downloading `json-search`, which can be tricky to install, **If unsure, we recommend using this by default**
Expand Down
25 changes: 25 additions & 0 deletions bin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# funclist

usage:
```
./funclist api.json
```

Look in an OpenAPI JSON file for the list of functions. To do this, identify which objects in components.schemas end with 'Request'. Each function call is separated by a space.

# line_check

usage:
```
./line_check "test_keywork" "this_arg" <<< "there is this_arg in this line"
```

Will return "this_arg".

This utility was created to avoid calling grep for each keyword we need to check. With this utility, we can pass all our keywords to line_check and then switch based on which keyword was found.

This is also much faster than multiple calls to grep.

# osc-api-seems-valid.sh

This utility is used to check if osc-api.json has been generated correctly. It verifies that the file is a valid JSON and contains at least one API function.
75 changes: 58 additions & 17 deletions bin/funclist.c
Original file line number Diff line number Diff line change
@@ -1,34 +1,75 @@
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "json.h"

int main(int ac, char **av)
{
struct json_object *j_file = json_object_from_file(av[1]);
bool funclist_from_path = 0;
struct json_object *j_file = NULL;

struct json_object *compo;
struct json_object *schema;
char *suffix = "Request";
int ret = 0;
ret = json_object_object_get_ex(j_file, "components", &compo);
if (!ret)
return 1;
json_object_object_get_ex(compo, "schemas", &schema);
if (!ret)
return 1;
int func_ret = 1;
int first = 1;
int i;

json_object_object_foreach(schema, k, v_) {
char *new_k = strdup(k);
char *resquest = strstr(new_k, "Request");
if (resquest) {
for (i = 1; i < ac; ++i) {
if (!strcmp(av[i], "--func-suffix")) {
++i;
if (i == ac) {
printf("usage: %s --func-suffix SUFFIX", av[0]);
return 1;
}
suffix = av[i];
} else if (!j_file) {
j_file = json_object_from_file(av[1]);
} else {
funclist_from_path = 1;
}
}

if (!funclist_from_path) {
struct json_object *compo;
struct json_object *schema;

ret = json_object_object_get_ex(j_file, "components", &compo);
if (!ret)
goto err;
json_object_object_get_ex(compo, "schemas", &schema);
if (!ret)
goto err;

json_object_object_foreach(schema, k, v_) {
char *new_k = strdup(k);
char *resquest = strstr(new_k, suffix);

if (resquest) {
if (!first)
putchar(' ');
first = 0;
*resquest = 0;
printf("%s", new_k);
}
free(new_k);
}
} else {
struct json_object *paths;

ret = json_object_object_get_ex(j_file, "paths", &paths);
if (!ret)
goto err;

json_object_object_foreach(paths, k, v_) {
if (!first)
putchar(' ');
first = 0;
*resquest = 0;
printf("%s", new_k);
printf("%s", k+1);
}
free(new_k);
}

func_ret = 0;
err:
json_object_put(j_file);
return 0;
return func_ret;
}
9 changes: 7 additions & 2 deletions bin/osc-api-seems-valid.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ config_path=$(realpath $(dirname $0))/../config.sh
shopt -s expand_aliases
source $config_path

test=$(json-search -sn Request $1 | json-search -n properties)
test_ret=$?
if [ -z "${FUNCTION_SUFFIX}" ]; then
test=$(json-search -n properties $1)
test_ret=$?
else
test=$(json-search -sn ${FUNCTION_SUFFIX} $1 | json-search -n properties)
test_ret=$?
fi

if [ "$test_ret" == "1" -o "$test" == "null" ]; then
echo "$1 is invalide" >&2
Expand Down
38 changes: 31 additions & 7 deletions cognac_gen.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ shopt -s expand_aliases

source ./helper.sh

debug "debug mode is on"

dash_this_arg()
{
local cur="$1"
Expand Down Expand Up @@ -210,15 +212,17 @@ replace_args()
arg_check=$(bin/line_check ____args____ ____func_code____ ____functions_proto____ ____cli_parser____ ____complex_struct_func_parser____ ____complex_struct_to_string_func____ ____call_list_dec____ ____call_list_descriptions____ ____call_list_args_descriptions____ <<< "$line")

if [ "$arg_check" == "____args____" ]; then
debug "____args____"
./mk_args.${lang}.sh
elif [ "$arg_check" == "____call_list_descriptions____" ]; then
debug "____call_list_descriptions____"
DELIMES=$(cut -d '(' -f 2 <<< $line | tr -d ')')
D1=$(cut -d ';' -f 1 <<< $DELIMES | tr -d "'")
D2=$(cut -d ';' -f 2 <<< $DELIMES | tr -d "'")
D3=$(cut -d ';' -f 3 <<< $DELIMES | tr -d "'")
for x in $CALL_LIST ; do
echo -en $D1
local required=$(json-search ${x}Request < osc-api.json | json-search required 2>&1 | tr -d '[]\n"' | tr -s ' ' | sed 's/nothing found//g')
local required=$(json-search ${x}${FUNCTION_SUFFIX} < osc-api.json | json-search required 2>&1 | tr -d '[]\n"' | tr -s ' ' | sed 's/nothing found//g')
local usage_required=$( for a in $(echo $required | tr -d ','); do echo -n " --${a}=${a,,}"; done )
local usage="\"Usage: oapi-cli $x ${usage_required} [OPTIONS]\n\""
local call_desc=$(jq .paths.\""/$x"\".description < osc-api.json | sed 's/<br \/>//g' | tr -d '"' | fold -s | sed 's/^/"/;s/$/\\n"/')
Expand All @@ -228,12 +232,13 @@ replace_args()
done
echo -ne $D3
elif [ "$arg_check" == "____call_list_args_descriptions____" ]; then
debug "____call_list_args_descriptions____"
DELIMES=$(cut -d '(' -f 2 <<< $line | tr -d ')')
D1=$(cut -d ';' -f 1 <<< $DELIMES | tr -d "'")
D2=$(cut -d ';' -f 2 <<< $DELIMES | tr -d "'")
D3=$(cut -d ';' -f 3 <<< $DELIMES | tr -d "'")
for x in $CALL_LIST ; do
st_info=$(json-search -s ${x}Request < osc-api.json)
st_info=$(json-search -s ${x}${FUNCTION_SUFFIX} < osc-api.json)
A_LST=$(json-search -K properties <<< $st_info | tr -d '",[]')

echo -en $D1
Expand All @@ -247,6 +252,7 @@ replace_args()
done
echo -ne $D3
elif [ "$arg_check" == "____call_list_dec____" ]; then
debug "____call_list_dec____"
DELIMES=$(cut -d '(' -f 2 <<< $line | tr -d ')')
D1=$(cut -d ';' -f 1 <<< $DELIMES | tr -d "'")
D2=$(cut -d ';' -f 2 <<< $DELIMES | tr -d "'")
Expand All @@ -258,7 +264,17 @@ replace_args()
done
echo -ne $D3
elif [ "$arg_check" == "____complex_struct_to_string_func____" ]; then
COMPLEX_STRUCT=$(jq .components < osc-api.json | json-search -KR schemas | tr -d '"' | sed 's/,/\n/g' | grep -v Response | grep -v Request)
debug "____complex_struct_to_string_func____"
local CALLS=$(for c in $(cat call_list) ; do echo ${c}${FUNCTION_SUFFIX} ; done)
COMPLEX_STRUCT=$(jq .components < osc-api.json | json-search -KR schemas | tr -d '"' | sed 's/,/\n/g' | grep -v Response)
local DIFF=$(echo ${CALLS[@]} ${COMPLEX_STRUCT[@]} | tr ' ' '\n' | sort | uniq -u)

debug "CALL_LIST: $CALLS"
debug "COMPLEX_STRUCT: $COMPLEX_STRUCT"
debug "diff:" "$DIFF"

COMPLEX_STRUCT="$DIFF"
debug "COMPLEX_STRUCT NOW:" "$COMPLEX_STRUCT"

for s in $COMPLEX_STRUCT; do
struct_name=$(to_snakecase <<< $s)
Expand Down Expand Up @@ -287,7 +303,12 @@ EOF
fi
done
elif [ "$arg_check" == "____complex_struct_func_parser____" ]; then
COMPLEX_STRUCT=$(jq .components < osc-api.json | json-search -KR schemas | tr -d '"' | sed 's/,/\n/g' | grep -v Response | grep -v Request)
debug "____complex_struct_func_parser____"
local CALLS=$(for c in $(cat call_list) ; do echo ${c}${FUNCTION_SUFFIX} ; done)
COMPLEX_STRUCT=$(jq .components < osc-api.json | json-search -KR schemas | tr -d '"' | sed 's/,/\n/g' | grep -v Response)
local DIFF=$(echo ${CALLS[@]} ${COMPLEX_STRUCT[@]} | tr ' ' '\n' | sort | uniq -u)

COMPLEX_STRUCT="$DIFF"

# prototypes
for s in $COMPLEX_STRUCT; do
Expand Down Expand Up @@ -349,9 +370,10 @@ EOF
done

elif [ "$arg_check" == "____cli_parser____" ] ; then
debug "____cli_parser____"
for l in $CALL_LIST; do
snake_l=$(to_snakecase <<< $l)
arg_list=$(json-search ${l}Request < osc-api.json \
arg_list=$(json-search ${l}${FUNCTION_SUFFIX} < osc-api.json \
| json-search -K properties \
| tr -d "[]\"," | sed '/^$/d')

Expand Down Expand Up @@ -457,14 +479,16 @@ EOF
EOF
done
elif [ "$arg_check" == "____functions_proto____" ] ; then
debug "____functions_proto____"
for l in $CALL_LIST; do
local snake_l=$(to_snakecase <<< $l)
echo "int osc_${snake_l}(struct osc_env *e, struct osc_str *out, struct osc_${snake_l}_arg *args);"
done
elif [ "$arg_check" == "____func_code____" ]; then
debug "____func_code____"
for x in $CALL_LIST; do
local snake_x=$(to_snakecase <<< $x)
local args=$(json-search ${x}Request < osc-api.json \
local args=$(json-search ${x}${FUNCTION_SUFFIX} < osc-api.json \
| json-search -K properties | tr -d "[]\",")
dashed_args=""
for arg in $args; do
Expand All @@ -481,7 +505,7 @@ EOF
done < function.${lang}
done
else
sed "s/____call_list____/${CALL_LIST}/g;s/____piped_call_list____/${PIPED_CALL_LIST}/;s/____api_version____/${API_VERSION}/g;s/____sdk_version____/${SDK_VERSION}/g;s/____cli_version____/$(cat cli-version)/g;s/____cli_name____/${CLI_NAME}/" <<< "$line";
sed "s/____call_list____/${CALL_LIST}/g;s/____piped_call_list____/${PIPED_CALL_LIST}/;s/____api_version____/${API_VERSION}/g;s/____sdk_version____/${SDK_VERSION}/g;s/____cli_version____/$(cat cli-version)/g;s/____cli_name____/${CLI_NAME}/" <<< "$line"
fi
done < $1
}
Expand Down
Loading
Loading