1
0
mirror of https://github.com/owenthereal/jqplay.git synced 2025-04-19 06:02:17 +03:00

Don't require JSON input when null input is enabled (#174)

* Dont' require JSON input when null input is enabled

Fixes #76 and also based a but on #85 by @zstadler

* Add `docker compose watch` for easier development

* Trigger run when query & `--null-input` are both not empty

* Add test test to validate `--null-input`

* Rename error with `Err` prefix

---------

Co-authored-by: Owen Ou <o@owenou.com>
This commit is contained in:
Mattias Wadman 2024-01-10 05:27:21 +01:00 committed by GitHub
parent 212a0e29f2
commit 1c739c7ff0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 106 additions and 16 deletions

View File

@ -37,4 +37,8 @@ setup:
.PHONY: start
start:
docker-compose up --build --force-recreate
docker compose up --build --force-recreate
.PHONY: watch
watch:
docker compose watch

View File

@ -58,7 +58,7 @@ angular.module('jqplay.controllers', []).controller('JqplayCtrl', function Jqpla
};
$scope.delayedRun = function (jq) {
if ($scope.input.$valid) {
if ($scope.input.$valid || (jq.q && jq.o.filter(f => f.name === "null-input" && f.enabled).length > 0)) {
if ($scope.runTimeout != null) {
$timeout.cancel($scope.runTimeout);
$scope.runTimeout = null;

View File

@ -10,6 +10,15 @@ services:
- "8080:8080"
environment:
DATABASE_URL: "postgres://jqplay-user:jqplay-pass@db/jqplay-db?sslmode=disable"
develop:
watch: &watch
- action: rebuild
path: .
ignore:
- .git
- .github
- build
- node_modules
db:
image: postgres:latest
restart: always

View File

@ -19,15 +19,15 @@ func (e *ValidationError) Error() string {
}
var (
ExecTimeoutError = errors.New("jq execution was timeout")
ExecCancelledError = errors.New("jq execution was cancelled")
allowedOpts = map[string]struct{}{
"slurp": struct{}{},
"null-input": struct{}{},
"compact-output": struct{}{},
"raw-input": struct{}{},
"raw-output": struct{}{},
"sort-keys": struct{}{},
ErrExecTimeout = errors.New("jq execution was timeout")
ErrExecCancelled = errors.New("jq execution was cancelled")
allowedOpts = map[string]struct{}{
"slurp": {},
"null-input": {},
"compact-output": {},
"raw-input": {},
"raw-output": {},
"sort-keys": {},
}
)
@ -37,11 +37,24 @@ type JQ struct {
O []JQOpt `json:"o"`
}
func (j *JQ) optIsEnabled(name string) bool {
for _, o := range j.O {
if o.Name == name {
return o.Enabled
}
}
return false
}
type JQOpt struct {
Name string `json:"name"`
Enabled bool `json:"enabled"`
}
func (o *JQOpt) String() string {
return fmt.Sprintf("%s (%t)", o.Name, o.Enabled)
}
func (j *JQ) Opts() []string {
opts := []string{}
for _, opt := range j.O {
@ -70,10 +83,10 @@ func (j *JQ) Eval(ctx context.Context, w io.Writer) error {
if err != nil {
ctxErr := ctx.Err()
if ctxErr == context.DeadlineExceeded {
return ExecTimeoutError
return ErrExecTimeout
}
if ctxErr == context.Canceled {
return ExecCancelledError
return ErrExecCancelled
}
}
@ -87,7 +100,7 @@ func (j *JQ) Validate() error {
errMsgs = append(errMsgs, "missing filter")
}
if j.J == "" {
if j.J == "" && !j.optIsEnabled("null-input") {
errMsgs = append(errMsgs, "missing JSON")
}

View File

@ -2,6 +2,7 @@ package jq
import (
"context"
"fmt"
"io"
"log"
"os"
@ -28,6 +29,69 @@ func TestJQEvalInvalidInput(t *testing.T) {
}
}
func TestJQNullInputOption(t *testing.T) {
cases := []struct {
J string
Q string
O []JQOpt
ErrStr string
}{
{
Q: ".",
O: []JQOpt{
{
Name: "null-input",
Enabled: true,
},
},
},
{
O: []JQOpt{
{
Name: "null-input",
Enabled: true,
},
},
ErrStr: "missing filter",
},
{
J: `{"foo": "bar"}`,
O: []JQOpt{
{
Name: "null-input",
Enabled: true,
},
},
ErrStr: "missing filter",
},
}
for _, c := range cases {
c := c
t.Run(fmt.Sprintf("j=%q q=%q o=%v", c.J, c.Q, c.O), func(t *testing.T) {
jq := &JQ{
J: c.J,
Q: c.Q,
O: c.O,
}
err := jq.Validate()
if err == nil && c.ErrStr != "" {
t.Errorf("err should not be nil: %s", c.ErrStr)
}
if err != nil && c.ErrStr == "" {
t.Errorf("err should be nil: %s", err)
}
if err != nil && c.ErrStr != "" {
if want, got := c.ErrStr, err.Error(); !strings.Contains(got, want) {
t.Errorf(`err not equal: want=%v got=%v`, want, got)
}
}
})
}
}
func TestJQValidateDisallowOpts(t *testing.T) {
jq := &JQ{
J: "{}",
@ -56,7 +120,7 @@ func TestJQEvalTimeout(t *testing.T) {
err := jq.Eval(ctx, io.Discard)
cancel()
if err != ExecTimeoutError {
if err != ErrExecTimeout {
t.Errorf("err message should be jq execution timeout, but it's %s", err)
}
}
@ -75,7 +139,7 @@ func TestJQEvalCancelled(t *testing.T) {
}()
err := jq.Eval(ctx, io.Discard)
if err != ExecCancelledError {
if err != ErrExecCancelled {
t.Errorf("err message should be jq execution timeout, but it's %s", err)
}
}