Gostaria de preparar um teste para um servidor REST petstore por meio de curl
.
Para testar sua add new pet
API, preparo uma string json que contém o Pet
para inserir em uma função bash que executa curl
:
#!/bin/bash
# Add new pet
add_petstore() {
local rest_url=$1
local id_pet="$2"
local id_category="$3"
local category_name="$4"
local pet_name="$5"
local photo_url="$6"
local tag_id="$7"
local tag_name="$8"
local pet_to_add="{ \
\"id\": $id_pet, \
\"category\": { \
\"id\": $id_category, \
\"name\": \"$category_name\" \
}, \
\"name\": \"$pet_name\", \
\"photoUrls\": [ \
\"$photo_url\" \
], \
\"tags\": [ \
{
\"id\": $tag_id, \
\"name\": \"$tag_name\" \
} \
] \
}"
echo "$pet_to_add"
curl -X 'POST' \
"$rest_url" \
-H 'accept: application/xml' \
-H 'Content-Type: application/json' \
-d "$pet_to_add"
}
add_petstore "http://localhost:8080/pet" "151" "12" "Dogs" "REX" "http://photosofrex/rex_rage.jpg" "1" "ferocious sales"
O echo
de $pet_to_add
parece o que eu estou disposto a ter:
{ "id": 151, "category": { "id": 12, "name": "Dogs" }, "name": "REX", "photoUrls": [ "http://photosofrex/rex_rage.jpg" ], "tags": [ {
"id": 1, "name": "ferocious sales" } ] }
e add_petstore
o método me permite preparar facilmente alguns animais de estimação.
Mas a local pet_to_add=...
declaração é muito suja.
Se alguém (ou eu mais tarde) precisar adaptar esse script, essa variável local não será acolhedora.
Pensei primeiro que poderia colocar seu conteúdo em um arquivo e lê-lo com uma extensão local pet_to_add=$(cat myfile)
. Mas isso não resolveria seus parâmetros variáveis.
Tenho uma maneira de escrever essa local pet_to_add
declaração de maneira mais limpa?
Sim, existe uma maneira mais limpa para muitos tipos de documentos JSON (embora não para todos os tipos), um documento aqui:
Para esse tipo de caso de uso, penso em um documento aqui como uma forma de string entre aspas duplas porque expande variáveis de shell/ambiente no documento, mas não remove caracteres de aspas duplas porque eles não são os delimitadores de string.
Quando o seu documento JSON é pequeno, você pode usar o
printf
comando interno para inserir valores no meio do documento e salvar o JSON resultante em uma variável shell:Usando
jq
para criar partes de JSON para cada subelemento do documento de carga útil de nível superior e, em seguida, juntar tudo. Dessa forma, você garante quejq
terá a oportunidade de codificar cada string e evita injetar variáveis de shell no JSON (o que pode quebrar o documento; veja, por exemplo, o$pet_name
valor usado abaixo).Primeiro, você configuraria as variáveis do shell com seus valores. Você pode usar valores lidos na linha de comando.
Em seguida, criamos o JSON para a
category
parte:Isso criaria um único objeto JSON com chaves
id
ename
. Os valores da chave são obtidos das variáveis do shell fornecidas. A$ARGS.named
coisa é um objeto internojq
contendo todas as chaves + valores fornecidos na linha de comando usando--arg
ou--argjson
(para valores que não deveriam ser, ou já são, codificados em string). Como ajq
expressão está entre aspas simples, o shell não será confundido$ARGS
com uma variável de shell.Em seguida, criamos o array de URL da foto para a
photoUrls
peça:Isso usa
jq
with--args
, que preenche a$ARGS.positional
matriz interna com os valores fornecidos. Observe que--args
e sua lista de argumentos deve sempre ocorrer por último na linha de comando.A seguir, criamos a
tags
parte do nosso array associativo:Criamos os elementos do array a partir das chaves e valores do array shell associativo em um loop e os inserimos em um array com
jq -s
leitura do loop.Se os IDs das suas tags forem numéricos, faça
tags
como umabash
matriz comum:... e depois converta-o em JSON usando
Observe
--argjson id "$tag_id"
no lugar de--arg id "$tag_id"
passar o número, que não deve ser convertido em string.Podemos então finalmente reunir a carga JSON destas partes:
O
bash
script acima geraria um documento JSON equivalente ao seguinte (mas em formato compacto):No momento, você está criando uma string que é uma mistura de formato (nomes de campos, caracteres de estrutura como
{
and[
, etc.) e dados (argumentos para a função). Eu separaria os dados do formato e usaria um documento aqui para preencher esse formato usando-
o delimitador de início do documento aqui para fornecer recuo de tabulação (guias iniciais no código onde você preenche a string não aparecem na saída) e quebra automática o aqui delimitador de documento em'
s para que todo o formato seja tratado como uma string entre aspas simples para protegê-lo de qualquer possível expansão do shell (por exemplo,Cost $5
não expandiria$5
como um parâmetro posicional) e, em seguida, deixe"$@"
expandir para preencher os valores do espaço reservado%s
s no formato que, como benefício colateral, significa que você não precisa de todas as outras variáveis locais:Os espaços iniciais no documento aqui são tabulações, portanto serão ignorados, cortesia do
-
in<<-
. Se você realmente deseja essas guias em sua saída, mude<<-
para apenas<<
.Se você realmente não deseja novas linhas,
pet_to_add
mas deseja usá-las ao preencher a string de formato, basta alterar isto:para isso:
e então você obterá esta saída:
e se você quiser comprimir todas as cadeias de espaços em branco contíguos em caracteres em branco únicos, então isto:
produziria esta saída:
A questão é que você pode massagear trivialmente o formato da maneira que quiser antes de preenchê-lo,
pet_to_add
sem afetar os dados (e vice-versa), porque dissociamos os dados do formato.