@voxgig/sdkgen 4.31.1 → 4.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/voxgig-sdkgen +1 -1
- package/dist/helpers/definitionPlan.d.ts +5 -0
- package/dist/helpers/definitionPlan.js +44 -7
- package/dist/helpers/definitionPlan.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/project/.sdk/tm/c/utility/make_url.c +11 -0
- package/project/.sdk/tm/c/utility/prepare_headers.c +53 -3
- package/project/.sdk/tm/c/utility/prepare_query.c +4 -1
- package/project/.sdk/tm/c/utility/transform_request.c +31 -8
- package/project/.sdk/tm/clojure/src/sdk/core.clj +62 -15
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +72 -15
- package/project/.sdk/tm/csharp/utility/MakeUrl.cs +8 -0
- package/project/.sdk/tm/csharp/utility/PrepareHeaders.cs +34 -4
- package/project/.sdk/tm/csharp/utility/PrepareQuery.cs +14 -0
- package/project/.sdk/tm/csharp/utility/TransformRequest.cs +31 -5
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +86 -24
- package/project/.sdk/tm/go/utility/make_url.go +8 -0
- package/project/.sdk/tm/go/utility/prepare_headers.go +36 -7
- package/project/.sdk/tm/go/utility/prepare_query.go +11 -0
- package/project/.sdk/tm/go/utility/transform_request.go +31 -5
- package/project/.sdk/tm/java/utility/MakeUrl.java +8 -0
- package/project/.sdk/tm/java/utility/PrepareHeaders.java +30 -6
- package/project/.sdk/tm/java/utility/PrepareQuery.java +10 -0
- package/project/.sdk/tm/java/utility/TransformRequest.java +30 -5
- package/project/.sdk/tm/js/src/Response.js +14 -1
- package/project/.sdk/tm/js/src/utility/MakeUrlUtility.js +7 -0
- package/project/.sdk/tm/js/src/utility/PrepareHeadersUtility.js +18 -0
- package/project/.sdk/tm/js/src/utility/PrepareQueryUtility.js +3 -1
- package/project/.sdk/tm/js/src/utility/TransformRequestUtility.js +17 -3
- package/project/.sdk/tm/js/test/definition-runner.js +74 -13
- package/project/.sdk/tm/js/test/utility/PrimaryUtility.test.js +25 -0
- package/project/.sdk/tm/kotlin/utility/MakeSpecUrl.kt +8 -0
- package/project/.sdk/tm/kotlin/utility/Prepare.kt +34 -4
- package/project/.sdk/tm/kotlin/utility/ResultTransform.kt +18 -6
- package/project/.sdk/tm/lua/utility/make_url.lua +8 -0
- package/project/.sdk/tm/lua/utility/prepare_headers.lua +35 -6
- package/project/.sdk/tm/lua/utility/prepare_query.lua +10 -0
- package/project/.sdk/tm/lua/utility/transform_request.lua +37 -6
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +63 -13
- package/project/.sdk/tm/perl/utility/make_url.pm +6 -0
- package/project/.sdk/tm/perl/utility/prepare_headers.pm +21 -3
- package/project/.sdk/tm/perl/utility/prepare_query.pm +8 -0
- package/project/.sdk/tm/perl/utility/transform_request.pm +19 -7
- package/project/.sdk/tm/php/utility/MakeUrl.php +7 -0
- package/project/.sdk/tm/php/utility/PrepareHeaders.php +33 -4
- package/project/.sdk/tm/php/utility/PrepareQuery.php +11 -0
- package/project/.sdk/tm/php/utility/TransformRequest.php +28 -5
- package/project/.sdk/tm/py/pkg/utility/make_url.py +7 -0
- package/project/.sdk/tm/py/pkg/utility/prepare_headers.py +27 -6
- package/project/.sdk/tm/py/pkg/utility/prepare_query.py +8 -0
- package/project/.sdk/tm/py/pkg/utility/transform_request.py +21 -5
- package/project/.sdk/tm/rb/utility/make_url.rb +7 -0
- package/project/.sdk/tm/rb/utility/prepare_headers.rb +21 -3
- package/project/.sdk/tm/rb/utility/prepare_query.rb +8 -0
- package/project/.sdk/tm/rb/utility/transform_request.rb +18 -5
- package/project/.sdk/tm/rust/utility/make_url.rs +10 -1
- package/project/.sdk/tm/rust/utility/prepare_headers.rs +43 -7
- package/project/.sdk/tm/rust/utility/prepare_query.rs +18 -1
- package/project/.sdk/tm/rust/utility/transform_request.rs +29 -7
- package/project/.sdk/tm/scala/utility/Make.scala +9 -0
- package/project/.sdk/tm/scala/utility/Prepare.scala +42 -3
- package/project/.sdk/tm/scala/utility/Transform.scala +30 -6
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Make.swift +7 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Prepare.swift +28 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/ResultUtil.swift +23 -5
- package/project/.sdk/tm/ts/src/Response.ts +14 -1
- package/project/.sdk/tm/ts/src/utility/MakeUrlUtility.ts +7 -0
- package/project/.sdk/tm/ts/src/utility/PrepareHeadersUtility.ts +16 -0
- package/project/.sdk/tm/ts/src/utility/PrepareQueryUtility.ts +3 -1
- package/project/.sdk/tm/ts/src/utility/TransformRequestUtility.ts +16 -3
- package/project/.sdk/tm/ts/test/definition-runner.ts +75 -13
- package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +25 -0
- package/project/.sdk/tm/zig/core/utility.zig +80 -20
- package/project/sdkgen-package.json +1 -1
- package/src/helpers/definitionPlan.ts +49 -7
|
@@ -17,24 +17,36 @@ our %REGISTRY;
|
|
|
17
17
|
|
|
18
18
|
# `$action` selects the point (see make_point); it is never an API field, so
|
|
19
19
|
# the body is a copy without it. The caller's hash is left untouched.
|
|
20
|
-
my $
|
|
21
|
-
my ($reqdata) = @_;
|
|
22
|
-
return $reqdata unless 'HASH' eq ref $reqdata && exists $reqdata->{
|
|
20
|
+
my $omit = sub {
|
|
21
|
+
my ($reqdata, @names) = @_;
|
|
22
|
+
return $reqdata unless 'HASH' eq ref $reqdata && grep { exists $reqdata->{$_} } @names;
|
|
23
23
|
my %body = %$reqdata;
|
|
24
|
-
delete
|
|
24
|
+
delete @body{@names};
|
|
25
25
|
return \%body;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
my $strip_action = sub { $omit->($_[0], '$action') };
|
|
29
|
+
|
|
30
|
+
# A header argument travels as a header, which prepare_headers sends, so the
|
|
31
|
+
# body is built from the request data without it.
|
|
32
|
+
my $header_arg_names = sub {
|
|
33
|
+
my ($point) = @_;
|
|
34
|
+
my $hl = $point ? ProjectNameHelpers::gpath($point, 'args.header') : undef;
|
|
35
|
+
return () unless Voxgig::Struct::islist($hl);
|
|
36
|
+
return grep { defined $_ && !ref $_ && '' ne $_ } map { ProjectNameHelpers::gp($_, 'name') } @$hl;
|
|
37
|
+
};
|
|
38
|
+
|
|
28
39
|
$REGISTRY{transform_request} = sub {
|
|
29
40
|
my ($ctx) = @_;
|
|
30
41
|
my $spec = $ctx->{spec};
|
|
31
42
|
my $point = $ctx->{point};
|
|
32
43
|
$spec->{step} = 'reqform' if $spec;
|
|
44
|
+
my $data = $omit->($ctx->{reqdata}, $header_arg_names->($point));
|
|
33
45
|
my $transform = ProjectNameHelpers::to_map(ProjectNameHelpers::gp($point, 'transform'));
|
|
34
|
-
return $strip_action->($
|
|
46
|
+
return $strip_action->($data) unless $transform;
|
|
35
47
|
my $reqform = ProjectNameHelpers::gp($transform, 'req');
|
|
36
|
-
return $strip_action->($
|
|
37
|
-
return $strip_action->(Voxgig::Struct::transform({ 'reqdata' => $
|
|
48
|
+
return $strip_action->($data) unless ProjectNameHelpers::rb_truthy($reqform);
|
|
49
|
+
return $strip_action->(Voxgig::Struct::transform({ 'reqdata' => $data }, $reqform));
|
|
38
50
|
};
|
|
39
51
|
|
|
40
52
|
1;
|
|
@@ -20,6 +20,13 @@ class ProjectNameMakeUrl
|
|
|
20
20
|
$url = \Voxgig\Struct\Struct::join([$spec->base, $spec->prefix, $spec->path, $spec->suffix], '/', true);
|
|
21
21
|
$resmatch = [];
|
|
22
22
|
|
|
23
|
+
// A route the definition ends with a slash keeps it: a server such as
|
|
24
|
+
// a Django REST one redirects or refuses the route without it.
|
|
25
|
+
$orig = $ctx->point ? \Voxgig\Struct\Struct::getprop($ctx->point, 'orig') : null;
|
|
26
|
+
if (is_string($orig) && str_ends_with($orig, '/') && '' === $spec->suffix && !str_ends_with($url, '/')) {
|
|
27
|
+
$url .= '/';
|
|
28
|
+
}
|
|
29
|
+
|
|
23
30
|
$param_items = \Voxgig\Struct\Struct::items($spec->params);
|
|
24
31
|
if ($param_items) {
|
|
25
32
|
foreach ($param_items as $item) {
|
|
@@ -9,10 +9,39 @@ class ProjectNamePrepareHeaders
|
|
|
9
9
|
{
|
|
10
10
|
$options = $ctx->client->options_map();
|
|
11
11
|
$headers = \Voxgig\Struct\Struct::getprop($options, 'headers');
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
$out = $headers ? \Voxgig\Struct\Struct::clone($headers) : [];
|
|
13
|
+
if (!is_array($out)) {
|
|
14
|
+
$out = [];
|
|
14
15
|
}
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
// A header parameter travels as a header, under the name the
|
|
17
|
+
// definition gives it, and only from this call's own arguments. It
|
|
18
|
+
// replaces a default of the same name, whatever its case.
|
|
19
|
+
$hl = $ctx->point ? \Voxgig\Struct\Struct::getpath($ctx->point, 'args.header') : null;
|
|
20
|
+
if (is_array($hl)) {
|
|
21
|
+
foreach ($hl as $hd) {
|
|
22
|
+
$name = \Voxgig\Struct\Struct::getprop($hd, 'name');
|
|
23
|
+
if (!is_string($name) || '' === $name) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
$orig = \Voxgig\Struct\Struct::getprop($hd, 'orig');
|
|
27
|
+
if (!is_string($orig) || '' === $orig) {
|
|
28
|
+
$orig = $name;
|
|
29
|
+
}
|
|
30
|
+
$val = \Voxgig\Struct\Struct::getprop($ctx->reqmatch ?? [], $name);
|
|
31
|
+
if (null === $val) {
|
|
32
|
+
$val = \Voxgig\Struct\Struct::getprop($ctx->reqdata ?? [], $name);
|
|
33
|
+
}
|
|
34
|
+
if (null !== $val) {
|
|
35
|
+
$wire = strtolower($orig);
|
|
36
|
+
foreach (array_keys($out) as $key) {
|
|
37
|
+
if (is_string($key) && strtolower($key) === $wire) {
|
|
38
|
+
unset($out[$key]);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
$out[$wire] = \Voxgig\Struct\Struct::stringify($val);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return $out;
|
|
17
46
|
}
|
|
18
47
|
}
|
|
@@ -27,6 +27,17 @@ class ProjectNamePrepareQuery
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
// A header parameter travels in the headers, which prepareHeaders
|
|
31
|
+
// fills.
|
|
32
|
+
$hl = \Voxgig\Struct\Struct::getpath($point, 'args.header');
|
|
33
|
+
if (is_array($hl)) {
|
|
34
|
+
foreach ($hl as $hd) {
|
|
35
|
+
$name = \Voxgig\Struct\Struct::getprop($hd, 'name');
|
|
36
|
+
if (is_string($name)) {
|
|
37
|
+
$params[] = $name;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
30
41
|
}
|
|
31
42
|
// A query parameter travels under the name the definition gives it,
|
|
32
43
|
// its orig, which the model may have renamed for the caller.
|
|
@@ -14,15 +14,16 @@ class ProjectNameTransformRequest
|
|
|
14
14
|
if ($spec) {
|
|
15
15
|
$spec->step = 'reqform';
|
|
16
16
|
}
|
|
17
|
+
$data = self::omit($ctx->reqdata, self::header_arg_names($point));
|
|
17
18
|
$transform = ProjectNameHelpers::to_map(\Voxgig\Struct\Struct::getprop($point, 'transform'));
|
|
18
19
|
if (!$transform) {
|
|
19
|
-
return self::strip_action($
|
|
20
|
+
return self::strip_action($data);
|
|
20
21
|
}
|
|
21
22
|
$reqform = \Voxgig\Struct\Struct::getprop($transform, 'req');
|
|
22
23
|
if (!$reqform) {
|
|
23
|
-
return self::strip_action($
|
|
24
|
+
return self::strip_action($data);
|
|
24
25
|
}
|
|
25
|
-
return self::strip_action(\Voxgig\Struct\Struct::transform(['reqdata' => $
|
|
26
|
+
return self::strip_action(\Voxgig\Struct\Struct::transform(['reqdata' => $data], $reqform));
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
// `$action` selects the point (see MakePoint); it is never an API field,
|
|
@@ -30,10 +31,32 @@ class ProjectNameTransformRequest
|
|
|
30
31
|
// never reaches the caller's copy.
|
|
31
32
|
private static function strip_action(mixed $reqdata): mixed
|
|
32
33
|
{
|
|
33
|
-
|
|
34
|
+
return self::omit($reqdata, ['$action']);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// A header argument travels as a header, which PrepareHeaders sends, so
|
|
38
|
+
// the body is built from the request data without it.
|
|
39
|
+
private static function header_arg_names(mixed $point): array
|
|
40
|
+
{
|
|
41
|
+
$hl = $point ? \Voxgig\Struct\Struct::getpath($point, 'args.header') : null;
|
|
42
|
+
$names = [];
|
|
43
|
+
foreach (is_array($hl) ? $hl : [] as $hd) {
|
|
44
|
+
$name = \Voxgig\Struct\Struct::getprop($hd, 'name');
|
|
45
|
+
if (is_string($name) && '' !== $name) {
|
|
46
|
+
$names[] = $name;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return $names;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private static function omit(mixed $reqdata, array $names): mixed
|
|
53
|
+
{
|
|
54
|
+
if (!is_array($reqdata)) {
|
|
34
55
|
return $reqdata;
|
|
35
56
|
}
|
|
36
|
-
|
|
57
|
+
foreach ($names as $name) {
|
|
58
|
+
unset($reqdata[$name]);
|
|
59
|
+
}
|
|
37
60
|
return $reqdata;
|
|
38
61
|
}
|
|
39
62
|
}
|
|
@@ -18,6 +18,13 @@ def make_url_util(ctx):
|
|
|
18
18
|
url = vs.join([spec.base, spec.prefix, spec.path, spec.suffix], "/", True)
|
|
19
19
|
resmatch = {}
|
|
20
20
|
|
|
21
|
+
# A route the definition ends with a slash keeps it: a server such as a
|
|
22
|
+
# Django REST one redirects or refuses the route without it.
|
|
23
|
+
orig = vs.getprop(ctx.point, "orig") if ctx.point is not None else None
|
|
24
|
+
if isinstance(orig, str) and orig.endswith("/") and not spec.suffix \
|
|
25
|
+
and not url.endswith("/"):
|
|
26
|
+
url += "/"
|
|
27
|
+
|
|
21
28
|
param_items = vs.items(spec.params)
|
|
22
29
|
if param_items is not None:
|
|
23
30
|
for item in param_items:
|
|
@@ -8,10 +8,31 @@ def prepare_headers_util(ctx):
|
|
|
8
8
|
options = ctx.client.options_map()
|
|
9
9
|
headers = vs.getprop(options, "headers")
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
out = {}
|
|
12
|
+
if headers is not None:
|
|
13
|
+
cloned = vs.clone(headers)
|
|
14
|
+
if isinstance(cloned, dict):
|
|
15
|
+
out = cloned
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
# A header parameter travels as a header, under the name the definition
|
|
18
|
+
# gives it, and only from this call's own arguments. It replaces a default
|
|
19
|
+
# of the same name, whatever its case.
|
|
20
|
+
hl = vs.getpath(ctx.point, "args.header") if ctx.point is not None else None
|
|
21
|
+
if isinstance(hl, list):
|
|
22
|
+
for hd in hl:
|
|
23
|
+
name = vs.getprop(hd, "name")
|
|
24
|
+
if not isinstance(name, str) or name == "":
|
|
25
|
+
continue
|
|
26
|
+
orig = vs.getprop(hd, "orig")
|
|
27
|
+
if not isinstance(orig, str) or orig == "":
|
|
28
|
+
orig = name
|
|
29
|
+
val = vs.getprop(ctx.reqmatch or {}, name)
|
|
30
|
+
if val is None:
|
|
31
|
+
val = vs.getprop(ctx.reqdata or {}, name)
|
|
32
|
+
if val is not None:
|
|
33
|
+
wire = orig.lower()
|
|
34
|
+
for key in [k for k in out if isinstance(k, str) and k.lower() == wire]:
|
|
35
|
+
del out[key]
|
|
36
|
+
out[wire] = vs.stringify(val)
|
|
37
|
+
|
|
38
|
+
return out
|
|
@@ -29,6 +29,14 @@ def prepare_query_util(ctx):
|
|
|
29
29
|
name = vs.getprop(pd, "name")
|
|
30
30
|
if isinstance(name, str):
|
|
31
31
|
params.append(name)
|
|
32
|
+
# A header parameter travels in the headers, which prepare_headers
|
|
33
|
+
# fills.
|
|
34
|
+
hl = vs.getpath(point, "args.header")
|
|
35
|
+
if isinstance(hl, list):
|
|
36
|
+
for hd in hl:
|
|
37
|
+
name = vs.getprop(hd, "name")
|
|
38
|
+
if isinstance(name, str):
|
|
39
|
+
params.append(name)
|
|
32
40
|
|
|
33
41
|
# A query parameter travels under the name the definition gives it, its
|
|
34
42
|
# orig, which the model may have renamed for the caller.
|
|
@@ -8,9 +8,23 @@ from projectname_sdk.core.helpers import to_map
|
|
|
8
8
|
# `$action` selects the point (see make_point_util); it is never an API field,
|
|
9
9
|
# so the body is a copy without it. The caller's dict is left untouched.
|
|
10
10
|
def _strip_action(reqdata):
|
|
11
|
-
|
|
11
|
+
return _omit(reqdata, ["$action"])
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# A header argument travels as a header, which prepare_headers_util sends, so
|
|
15
|
+
# the body is built from the request data without it.
|
|
16
|
+
def _header_arg_names(point):
|
|
17
|
+
hl = vs.getpath(point, "args.header") if point is not None else None
|
|
18
|
+
if not isinstance(hl, list):
|
|
19
|
+
return []
|
|
20
|
+
names = [vs.getprop(hd, "name") for hd in hl]
|
|
21
|
+
return [n for n in names if isinstance(n, str) and n != ""]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _omit(reqdata, names):
|
|
25
|
+
if not isinstance(reqdata, dict) or not any(n in reqdata for n in names):
|
|
12
26
|
return reqdata
|
|
13
|
-
return {k: v for k, v in reqdata.items() if k
|
|
27
|
+
return {k: v for k, v in reqdata.items() if k not in names}
|
|
14
28
|
|
|
15
29
|
|
|
16
30
|
def transform_request_util(ctx):
|
|
@@ -20,16 +34,18 @@ def transform_request_util(ctx):
|
|
|
20
34
|
if spec is not None:
|
|
21
35
|
spec.step = "reqform"
|
|
22
36
|
|
|
37
|
+
data = _omit(ctx.reqdata, _header_arg_names(point))
|
|
38
|
+
|
|
23
39
|
transform = to_map(vs.getprop(point, "transform"))
|
|
24
40
|
if transform is None:
|
|
25
|
-
return _strip_action(
|
|
41
|
+
return _strip_action(data)
|
|
26
42
|
|
|
27
43
|
reqform = vs.getprop(transform, "req")
|
|
28
44
|
if reqform is None:
|
|
29
|
-
return _strip_action(
|
|
45
|
+
return _strip_action(data)
|
|
30
46
|
|
|
31
47
|
reqdata = vs.transform({
|
|
32
|
-
"reqdata":
|
|
48
|
+
"reqdata": data,
|
|
33
49
|
}, reqform)
|
|
34
50
|
|
|
35
51
|
return _strip_action(reqdata)
|
|
@@ -11,6 +11,13 @@ module ProjectNameUtilities
|
|
|
11
11
|
url = VoxgigStruct.join([spec.base, spec.prefix, spec.path, spec.suffix], "/", true)
|
|
12
12
|
resmatch = {}
|
|
13
13
|
|
|
14
|
+
# A route the definition ends with a slash keeps it: a server such as a
|
|
15
|
+
# Django REST one redirects or refuses the route without it.
|
|
16
|
+
orig = ctx.point ? VoxgigStruct.getprop(ctx.point, "orig") : nil
|
|
17
|
+
if orig.is_a?(String) && orig.end_with?("/") && spec.suffix.to_s.empty? && !url.end_with?("/")
|
|
18
|
+
url += "/"
|
|
19
|
+
end
|
|
20
|
+
|
|
14
21
|
param_items = VoxgigStruct.items(spec.params)
|
|
15
22
|
if param_items
|
|
16
23
|
param_items.each do |item|
|
|
@@ -4,8 +4,26 @@ module ProjectNameUtilities
|
|
|
4
4
|
PrepareHeaders = ->(ctx) {
|
|
5
5
|
options = ctx.client.options_map
|
|
6
6
|
headers = VoxgigStruct.getprop(options, "headers")
|
|
7
|
-
|
|
8
|
-
out =
|
|
9
|
-
|
|
7
|
+
out = headers ? VoxgigStruct.clone(headers) : {}
|
|
8
|
+
out = {} unless out.is_a?(Hash)
|
|
9
|
+
# A header parameter travels as a header, under the name the definition
|
|
10
|
+
# gives it, and only from this call's own arguments. It replaces a default
|
|
11
|
+
# of the same name, whatever its case.
|
|
12
|
+
hl = ctx.point ? VoxgigStruct.getpath(ctx.point, "args.header") : nil
|
|
13
|
+
if hl.is_a?(Array)
|
|
14
|
+
hl.each do |hd|
|
|
15
|
+
name = VoxgigStruct.getprop(hd, "name")
|
|
16
|
+
next unless name.is_a?(String) && !name.empty?
|
|
17
|
+
orig = VoxgigStruct.getprop(hd, "orig")
|
|
18
|
+
orig = name unless orig.is_a?(String) && !orig.empty?
|
|
19
|
+
val = VoxgigStruct.getprop(ctx.reqmatch || {}, name)
|
|
20
|
+
val = VoxgigStruct.getprop(ctx.reqdata || {}, name) if val.nil?
|
|
21
|
+
next if val.nil?
|
|
22
|
+
wire = orig.downcase
|
|
23
|
+
out.delete_if { |k, _| k.is_a?(String) && k.downcase == wire }
|
|
24
|
+
out[wire] = VoxgigStruct.stringify(val)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
out
|
|
10
28
|
}
|
|
11
29
|
end
|
|
@@ -17,6 +17,14 @@ module ProjectNameUtilities
|
|
|
17
17
|
params << name if name.is_a?(String)
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
|
+
# A header parameter travels in the headers, which prepare_headers fills.
|
|
21
|
+
hl = VoxgigStruct.getpath(point, "args.header")
|
|
22
|
+
if hl.is_a?(Array)
|
|
23
|
+
hl.each do |hd|
|
|
24
|
+
name = VoxgigStruct.getprop(hd, "name")
|
|
25
|
+
params << name if name.is_a?(String)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
20
28
|
end
|
|
21
29
|
# A query parameter travels under the name the definition gives it, its
|
|
22
30
|
# orig, which the model may have renamed for the caller.
|
|
@@ -5,18 +5,31 @@ module ProjectNameUtilities
|
|
|
5
5
|
# `$action` selects the point (see MakePoint); it is never an API field, so
|
|
6
6
|
# the body is a copy without it. The caller's hash is left untouched.
|
|
7
7
|
def self.strip_action(reqdata)
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
omit_keys(reqdata, ["$action"])
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# A header argument travels as a header, which PrepareHeaders sends, so the
|
|
12
|
+
# body is built from the request data without it.
|
|
13
|
+
def self.header_arg_names(point)
|
|
14
|
+
hl = point ? VoxgigStruct.getpath(point, "args.header") : nil
|
|
15
|
+
return [] unless hl.is_a?(Array)
|
|
16
|
+
hl.map { |hd| VoxgigStruct.getprop(hd, "name") }.select { |n| n.is_a?(String) && !n.empty? }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.omit_keys(reqdata, names)
|
|
20
|
+
return reqdata unless reqdata.is_a?(Hash) && names.any? { |n| reqdata.key?(n) }
|
|
21
|
+
reqdata.reject { |k, _| names.include?(k) }
|
|
10
22
|
end
|
|
11
23
|
|
|
12
24
|
TransformRequest = ->(ctx) {
|
|
13
25
|
spec = ctx.spec
|
|
14
26
|
point = ctx.point
|
|
15
27
|
spec.step = "reqform" if spec
|
|
28
|
+
data = ProjectNameUtilities.omit_keys(ctx.reqdata, ProjectNameUtilities.header_arg_names(point))
|
|
16
29
|
transform = ProjectNameHelpers.to_map(VoxgigStruct.getprop(point, "transform"))
|
|
17
|
-
return ProjectNameUtilities.strip_action(
|
|
30
|
+
return ProjectNameUtilities.strip_action(data) unless transform
|
|
18
31
|
reqform = VoxgigStruct.getprop(transform, "req")
|
|
19
|
-
return ProjectNameUtilities.strip_action(
|
|
20
|
-
ProjectNameUtilities.strip_action(VoxgigStruct.transform({ "reqdata" =>
|
|
32
|
+
return ProjectNameUtilities.strip_action(data) unless reqform
|
|
33
|
+
ProjectNameUtilities.strip_action(VoxgigStruct.transform({ "reqdata" => data }, reqform))
|
|
21
34
|
}
|
|
22
35
|
end
|
|
@@ -2,7 +2,7 @@ use std::rc::Rc;
|
|
|
2
2
|
|
|
3
3
|
use crate::core::context::Context;
|
|
4
4
|
use crate::core::error::ProjectNameError;
|
|
5
|
-
use crate::core::helpers::setp;
|
|
5
|
+
use crate::core::helpers::{getp, setp};
|
|
6
6
|
use crate::utility::voxgigstruct as vs;
|
|
7
7
|
use crate::utility::voxgigstruct::Value;
|
|
8
8
|
|
|
@@ -26,6 +26,7 @@ pub fn make_url_util(ctx: &Rc<Context>) -> Result<String, ProjectNameError> {
|
|
|
26
26
|
)
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
+
let suffixless = suffix.is_empty();
|
|
29
30
|
let mut url = vs::join(
|
|
30
31
|
&Value::list(vec![
|
|
31
32
|
Value::str(base),
|
|
@@ -37,6 +38,14 @@ pub fn make_url_util(ctx: &Rc<Context>) -> Result<String, ProjectNameError> {
|
|
|
37
38
|
true,
|
|
38
39
|
);
|
|
39
40
|
|
|
41
|
+
// A route the definition ends with a slash keeps it: a server such as a
|
|
42
|
+
// Django REST one redirects or refuses the route without it.
|
|
43
|
+
if let Value::Str(orig) = getp(&ctx.point.borrow().clone(), "orig") {
|
|
44
|
+
if orig.ends_with('/') && suffixless && !url.ends_with('/') {
|
|
45
|
+
url.push('/');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
40
49
|
let resmatch = Value::empty_map();
|
|
41
50
|
|
|
42
51
|
if let Value::Map(pm) = ¶ms {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
use std::rc::Rc;
|
|
2
2
|
|
|
3
3
|
use crate::core::context::Context;
|
|
4
|
-
use crate::core::helpers::getp;
|
|
4
|
+
use crate::core::helpers::{getp, setp};
|
|
5
5
|
use crate::utility::voxgigstruct as vs;
|
|
6
6
|
use crate::utility::voxgigstruct::Value;
|
|
7
7
|
|
|
@@ -12,12 +12,48 @@ pub fn prepare_headers_util(ctx: &Rc<Context>) -> Value {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
let headers = getp(&options, "headers");
|
|
15
|
-
if headers.is_noval() || headers.is_null() {
|
|
16
|
-
|
|
17
|
-
}
|
|
15
|
+
let out = if headers.is_noval() || headers.is_null() {
|
|
16
|
+
Value::empty_map()
|
|
17
|
+
} else {
|
|
18
|
+
match vs::clone(&headers) {
|
|
19
|
+
Value::Map(m) => Value::Map(m),
|
|
20
|
+
_ => Value::empty_map(),
|
|
21
|
+
}
|
|
22
|
+
};
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
// A header parameter travels as a header, under the name the definition
|
|
25
|
+
// gives it, and only from this call's own arguments. It replaces a default
|
|
26
|
+
// of the same name, whatever its case.
|
|
27
|
+
let point = ctx.point.borrow().clone();
|
|
28
|
+
if let Value::List(hl) = getp(&getp(&point, "args"), "header") {
|
|
29
|
+
let reqmatch = ctx.reqmatch.borrow().clone();
|
|
30
|
+
let reqdata = ctx.reqdata.borrow().clone();
|
|
31
|
+
for hd in hl.borrow().iter() {
|
|
32
|
+
let name = match getp(hd, "name") {
|
|
33
|
+
Value::Str(n) if !n.is_empty() => n,
|
|
34
|
+
_ => continue,
|
|
35
|
+
};
|
|
36
|
+
let wire = match getp(hd, "orig") {
|
|
37
|
+
Value::Str(o) if !o.is_empty() => o,
|
|
38
|
+
_ => name.clone(),
|
|
39
|
+
};
|
|
40
|
+
let mut val = getp(&reqmatch, &name);
|
|
41
|
+
if val.is_noval() || val.is_null() {
|
|
42
|
+
val = getp(&reqdata, &name);
|
|
43
|
+
}
|
|
44
|
+
if !val.is_noval() && !val.is_null() {
|
|
45
|
+
let key = wire.to_lowercase();
|
|
46
|
+
if let Value::Map(m) = &out {
|
|
47
|
+
let same: Vec<String> =
|
|
48
|
+
m.borrow().keys().filter(|k| k.to_lowercase() == key).cloned().collect();
|
|
49
|
+
for k in same {
|
|
50
|
+
m.borrow_mut().shift_remove(&k);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
setp(&out, &key, Value::Str(vs::stringify(&val, None, false)));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
22
56
|
}
|
|
57
|
+
|
|
58
|
+
out
|
|
23
59
|
}
|
|
@@ -45,6 +45,21 @@ pub fn prepare_query_util(ctx: &Rc<Context>) -> Value {
|
|
|
45
45
|
false
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
+
// A header parameter travels in the headers, which prepare_headers fills.
|
|
49
|
+
let aheader = getp(&getp(&point, "args"), "header");
|
|
50
|
+
let in_header = |key: &str| -> bool {
|
|
51
|
+
if let Value::List(hl) = &aheader {
|
|
52
|
+
for hd in hl.borrow().iter() {
|
|
53
|
+
if let Value::Str(s) = getp(hd, "name") {
|
|
54
|
+
if s == key {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
false
|
|
61
|
+
};
|
|
62
|
+
|
|
48
63
|
// A query parameter travels under the name the definition gives it, its
|
|
49
64
|
// orig, which the model may have renamed for the caller.
|
|
50
65
|
let aquery = getp(&getp(&point, "args"), "query");
|
|
@@ -69,7 +84,9 @@ pub fn prepare_query_util(ctx: &Rc<Context>) -> Value {
|
|
|
69
84
|
.map(|(k, v)| (k.clone(), v.clone()))
|
|
70
85
|
.collect();
|
|
71
86
|
for (key, val) in entries {
|
|
72
|
-
if !val.is_noval() && !val.is_null() && "$action" != key && !contains(&key) && !in_args(&key)
|
|
87
|
+
if !val.is_noval() && !val.is_null() && "$action" != key && !contains(&key) && !in_args(&key)
|
|
88
|
+
&& !in_header(&key)
|
|
89
|
+
{
|
|
73
90
|
setp(&out, &wire_name(&key), val);
|
|
74
91
|
}
|
|
75
92
|
}
|
|
@@ -8,8 +8,28 @@ use crate::utility::voxgigstruct::Value;
|
|
|
8
8
|
// `$action` selects the point (see make_point_util); it is never an API
|
|
9
9
|
// field, so the body is a copy without it. The caller's map is left untouched.
|
|
10
10
|
fn strip_action(reqdata: Value) -> Value {
|
|
11
|
+
omit_keys(reqdata, &["$action".to_string()])
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// A header argument travels as a header, which prepare_headers_util sends, so
|
|
15
|
+
// the body is built from the request data without it.
|
|
16
|
+
fn header_arg_names(point: &Value) -> Vec<String> {
|
|
17
|
+
match getp(&getp(point, "args"), "header") {
|
|
18
|
+
Value::List(hl) => hl
|
|
19
|
+
.borrow()
|
|
20
|
+
.iter()
|
|
21
|
+
.filter_map(|hd| match getp(hd, "name") {
|
|
22
|
+
Value::Str(n) if !n.is_empty() => Some(n),
|
|
23
|
+
_ => None,
|
|
24
|
+
})
|
|
25
|
+
.collect(),
|
|
26
|
+
_ => Vec::new(),
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fn omit_keys(reqdata: Value, names: &[String]) -> Value {
|
|
11
31
|
let has = match &reqdata {
|
|
12
|
-
Value::Map(src) => src.borrow().contains_key(
|
|
32
|
+
Value::Map(src) => names.iter().any(|n| src.borrow().contains_key(n)),
|
|
13
33
|
_ => false,
|
|
14
34
|
};
|
|
15
35
|
if !has {
|
|
@@ -23,7 +43,7 @@ fn strip_action(reqdata: Value) -> Value {
|
|
|
23
43
|
.map(|(k, v)| (k.clone(), v.clone()))
|
|
24
44
|
.collect();
|
|
25
45
|
for (k, v) in entries {
|
|
26
|
-
if
|
|
46
|
+
if !names.contains(&k) {
|
|
27
47
|
setp(&body, &k, v);
|
|
28
48
|
}
|
|
29
49
|
}
|
|
@@ -39,19 +59,21 @@ pub fn transform_request_util(ctx: &Rc<Context>) -> Value {
|
|
|
39
59
|
sp.borrow_mut().step = "reqform".to_string();
|
|
40
60
|
}
|
|
41
61
|
|
|
62
|
+
let reqdata = omit_keys(ctx.reqdata.borrow().clone(), &header_arg_names(&point));
|
|
63
|
+
|
|
42
64
|
let transform = to_map(&getp(&point, "transform"));
|
|
43
65
|
if transform.is_noval() {
|
|
44
|
-
return strip_action(
|
|
66
|
+
return strip_action(reqdata);
|
|
45
67
|
}
|
|
46
68
|
|
|
47
69
|
let reqform = getp(&transform, "req");
|
|
48
70
|
if reqform.is_noval() || reqform.is_null() {
|
|
49
|
-
return strip_action(
|
|
71
|
+
return strip_action(reqdata);
|
|
50
72
|
}
|
|
51
73
|
|
|
52
|
-
let store = jo(vec![("reqdata",
|
|
74
|
+
let store = jo(vec![("reqdata", reqdata.clone())]);
|
|
53
75
|
strip_action(match vs::transform(&store, &reqform, None) {
|
|
54
|
-
Ok(
|
|
55
|
-
Err(_) =>
|
|
76
|
+
Ok(out) => out,
|
|
77
|
+
Err(_) => reqdata,
|
|
56
78
|
})
|
|
57
79
|
}
|
|
@@ -549,6 +549,15 @@ object MakeUrl {
|
|
|
549
549
|
joinParts.add(spec.suffix)
|
|
550
550
|
var url = Struct.join(joinParts, "/", true)
|
|
551
551
|
|
|
552
|
+
// A route the definition ends with a slash keeps it: a server such as a
|
|
553
|
+
// Django REST one redirects or refuses the route without it.
|
|
554
|
+
val orig = if (ctx.point == null) null else Struct.getprop(ctx.point, "orig", null)
|
|
555
|
+
orig match {
|
|
556
|
+
case o: String if o.endsWith("/") && (spec.suffix == null || spec.suffix.isEmpty) && !url.endsWith("/") =>
|
|
557
|
+
url = url + "/"
|
|
558
|
+
case _ =>
|
|
559
|
+
}
|
|
560
|
+
|
|
552
561
|
val resmatch = new LinkedHashMap[String, Object]()
|
|
553
562
|
|
|
554
563
|
val pit = Struct.items(spec.params).iterator()
|