@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
|
@@ -1003,13 +1003,24 @@ defmodule ProjectName.Utility do
|
|
|
1003
1003
|
{"", Context.make_error(ctx, "url_no_result", "Expected context result property to be defined.")}
|
|
1004
1004
|
|
|
1005
1005
|
true ->
|
|
1006
|
-
|
|
1006
|
+
joined =
|
|
1007
1007
|
S.join(
|
|
1008
1008
|
S.jt([S.getprop(spec, "base"), S.getprop(spec, "prefix"), S.getprop(spec, "path"), S.getprop(spec, "suffix")]),
|
|
1009
1009
|
"/",
|
|
1010
1010
|
true
|
|
1011
1011
|
)
|
|
1012
1012
|
|
|
1013
|
+
# A route the definition ends with a slash keeps it: a server such as a
|
|
1014
|
+
# Django REST one redirects or refuses the route without it.
|
|
1015
|
+
orig = S.getprop(S.getprop(ctx, "point"), "orig")
|
|
1016
|
+
suffix = S.getprop(spec, "suffix")
|
|
1017
|
+
|
|
1018
|
+
url0 =
|
|
1019
|
+
if is_binary(orig) and String.ends_with?(orig, "/") and (suffix == nil or suffix == "") and
|
|
1020
|
+
not String.ends_with?(joined, "/"),
|
|
1021
|
+
do: joined <> "/",
|
|
1022
|
+
else: joined
|
|
1023
|
+
|
|
1013
1024
|
resmatch = S.jm([])
|
|
1014
1025
|
|
|
1015
1026
|
url1 =
|
|
@@ -1125,12 +1136,44 @@ defmodule ProjectName.Utility do
|
|
|
1125
1136
|
options = opts_map(S.getprop(ctx, "client"))
|
|
1126
1137
|
headers = S.getprop(options, "headers")
|
|
1127
1138
|
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1139
|
+
out =
|
|
1140
|
+
if headers == nil do
|
|
1141
|
+
S.jm([])
|
|
1142
|
+
else
|
|
1143
|
+
cloned = S.clone(headers)
|
|
1144
|
+
if S.ismap(cloned), do: cloned, else: S.jm([])
|
|
1145
|
+
end
|
|
1146
|
+
|
|
1147
|
+
# A header parameter travels as a header, under the name the definition
|
|
1148
|
+
# gives it, and only from this call's own arguments. It replaces a default
|
|
1149
|
+
# of the same name, whatever its case.
|
|
1150
|
+
point = S.getprop(ctx, "point")
|
|
1151
|
+
aheader = if point != nil, do: S.getpath(point, "args.header"), else: nil
|
|
1152
|
+
|
|
1153
|
+
if S.islist(aheader) and S.size(aheader) > 0 do
|
|
1154
|
+
Enum.each(0..(S.size(aheader) - 1), fn i ->
|
|
1155
|
+
hd = S.getelem(aheader, i)
|
|
1156
|
+
name = S.getprop(hd, "name")
|
|
1157
|
+
|
|
1158
|
+
if is_binary(name) and name != "" do
|
|
1159
|
+
orig = S.getprop(hd, "orig")
|
|
1160
|
+
wire = if is_binary(orig) and orig != "", do: orig, else: name
|
|
1161
|
+
val = S.getprop(S.getprop(ctx, "reqmatch"), name)
|
|
1162
|
+
val = if val == nil, do: S.getprop(S.getprop(ctx, "reqdata"), name), else: val
|
|
1163
|
+
if val != nil do
|
|
1164
|
+
key = String.downcase(wire)
|
|
1165
|
+
|
|
1166
|
+
Enum.each(H.entries(out), fn {k, _} ->
|
|
1167
|
+
if is_binary(k) and String.downcase(k) == key, do: S.delprop(out, k)
|
|
1168
|
+
end)
|
|
1169
|
+
|
|
1170
|
+
S.setprop(out, key, S.stringify(val))
|
|
1171
|
+
end
|
|
1172
|
+
end
|
|
1173
|
+
end)
|
|
1133
1174
|
end
|
|
1175
|
+
|
|
1176
|
+
out
|
|
1134
1177
|
end
|
|
1135
1178
|
|
|
1136
1179
|
def prepare_body_impl(ctx) do
|
|
@@ -1187,6 +1230,19 @@ defmodule ProjectName.Utility do
|
|
|
1187
1230
|
S.join(parts, "/", true)
|
|
1188
1231
|
end
|
|
1189
1232
|
|
|
1233
|
+
# The names in one of a point's lists of argument definitions.
|
|
1234
|
+
defp arg_names(nil, _path), do: []
|
|
1235
|
+
|
|
1236
|
+
defp arg_names(point, path) do
|
|
1237
|
+
defs = S.getpath(point, path)
|
|
1238
|
+
|
|
1239
|
+
if S.islist(defs) and S.size(defs) > 0 do
|
|
1240
|
+
Enum.map(0..(S.size(defs) - 1), fn i -> S.getprop(S.getelem(defs, i), "name") end)
|
|
1241
|
+
else
|
|
1242
|
+
[]
|
|
1243
|
+
end
|
|
1244
|
+
end
|
|
1245
|
+
|
|
1190
1246
|
def prepare_query_impl(ctx) do
|
|
1191
1247
|
point = S.getprop(ctx, "point")
|
|
1192
1248
|
reqmatch = H.or_(S.getprop(ctx, "reqmatch"), S.jm([]))
|
|
@@ -1208,18 +1264,8 @@ defmodule ProjectName.Utility do
|
|
|
1208
1264
|
|
|
1209
1265
|
# A path parameter travels in the path. The generated config lists them
|
|
1210
1266
|
# as args.params, which prepare_params reads; params is the older list.
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
arg_strs =
|
|
1214
|
-
if S.islist(aparams) and S.size(aparams) > 0 do
|
|
1215
|
-
Enum.map(0..(S.size(aparams) - 1), fn i ->
|
|
1216
|
-
S.getprop(S.getelem(aparams, i), "name")
|
|
1217
|
-
end)
|
|
1218
|
-
else
|
|
1219
|
-
[]
|
|
1220
|
-
end
|
|
1221
|
-
|
|
1222
|
-
param_strs = param_strs ++ arg_strs
|
|
1267
|
+
# A header parameter travels in the headers, which prepare_headers fills.
|
|
1268
|
+
param_strs = param_strs ++ arg_names(point, "args.params") ++ arg_names(point, "args.header")
|
|
1223
1269
|
|
|
1224
1270
|
# A query parameter travels under the name the definition gives it, its
|
|
1225
1271
|
# orig, which the model may have renamed for the caller.
|
|
@@ -1484,12 +1530,27 @@ defmodule ProjectName.Utility do
|
|
|
1484
1530
|
# `$action` selects the point (see make_point_impl); it is never an API
|
|
1485
1531
|
# field, so the body is a copy without it. The caller's map is left
|
|
1486
1532
|
# untouched.
|
|
1487
|
-
defp strip_action(reqdata) do
|
|
1488
|
-
|
|
1533
|
+
defp strip_action(reqdata), do: omit_keys(reqdata, ["$action"])
|
|
1534
|
+
|
|
1535
|
+
# A header argument travels as a header, which prepare_headers_impl sends, so
|
|
1536
|
+
# the body is built from the request data without it.
|
|
1537
|
+
defp header_arg_names(point) do
|
|
1538
|
+
aheader = if point != nil, do: S.getpath(point, "args.header"), else: nil
|
|
1539
|
+
|
|
1540
|
+
if S.islist(aheader) and S.size(aheader) > 0 do
|
|
1541
|
+
Enum.map(0..(S.size(aheader) - 1), &S.getprop(S.getelem(aheader, &1), "name"))
|
|
1542
|
+
|> Enum.filter(&(is_binary(&1) and &1 != ""))
|
|
1543
|
+
else
|
|
1544
|
+
[]
|
|
1545
|
+
end
|
|
1546
|
+
end
|
|
1547
|
+
|
|
1548
|
+
defp omit_keys(reqdata, names) do
|
|
1549
|
+
if S.ismap(reqdata) and Enum.any?(names, &S.haskey(reqdata, &1)) do
|
|
1489
1550
|
body = S.jm([])
|
|
1490
1551
|
|
|
1491
1552
|
Enum.each(H.entries(reqdata), fn {key, val} ->
|
|
1492
|
-
if key
|
|
1553
|
+
if key not in names, do: S.setprop(body, key, val)
|
|
1493
1554
|
end)
|
|
1494
1555
|
|
|
1495
1556
|
body
|
|
@@ -1503,18 +1564,19 @@ defmodule ProjectName.Utility do
|
|
|
1503
1564
|
point = S.getprop(ctx, "point")
|
|
1504
1565
|
if spec != nil, do: S.setprop(spec, "step", "reqform")
|
|
1505
1566
|
|
|
1567
|
+
data = omit_keys(S.getprop(ctx, "reqdata"), header_arg_names(point))
|
|
1506
1568
|
transform = H.to_map(S.getprop(point, "transform"))
|
|
1507
1569
|
|
|
1508
1570
|
reqdata =
|
|
1509
1571
|
if transform == nil do
|
|
1510
|
-
|
|
1572
|
+
data
|
|
1511
1573
|
else
|
|
1512
1574
|
reqform = S.getprop(transform, "req")
|
|
1513
1575
|
|
|
1514
1576
|
if reqform == nil do
|
|
1515
|
-
|
|
1577
|
+
data
|
|
1516
1578
|
else
|
|
1517
|
-
S.transform(S.jm(["reqdata",
|
|
1579
|
+
S.transform(S.jm(["reqdata", data]), reqform)
|
|
1518
1580
|
end
|
|
1519
1581
|
end
|
|
1520
1582
|
|
|
@@ -2,6 +2,7 @@ package utility
|
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
4
|
"regexp"
|
|
5
|
+
"strings"
|
|
5
6
|
|
|
6
7
|
vs "github.com/voxgig/struct"
|
|
7
8
|
|
|
@@ -24,6 +25,13 @@ func makeUrlUtil(ctx *core.Context) (string, error) {
|
|
|
24
25
|
url := vs.Join([]any{spec.Base, spec.Prefix, spec.Path, spec.Suffix}, "/", true)
|
|
25
26
|
resmatch := map[string]any{}
|
|
26
27
|
|
|
28
|
+
// A route the definition ends with a slash keeps it: a server such as a
|
|
29
|
+
// Django REST one redirects or refuses the route without it.
|
|
30
|
+
if orig, _ := vs.GetProp(ctx.Point, "orig").(string); strings.HasSuffix(orig, "/") &&
|
|
31
|
+
spec.Suffix == "" && !strings.HasSuffix(url, "/") {
|
|
32
|
+
url += "/"
|
|
33
|
+
}
|
|
34
|
+
|
|
27
35
|
params := spec.Params
|
|
28
36
|
for _, item := range vs.Items(params) {
|
|
29
37
|
key, _ := item[0].(string)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
package utility
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"strings"
|
|
5
|
+
|
|
4
6
|
vs "github.com/voxgig/struct"
|
|
5
7
|
|
|
6
8
|
"GOMODULE/core"
|
|
@@ -9,14 +11,41 @@ import (
|
|
|
9
11
|
func prepareHeadersUtil(ctx *core.Context) map[string]any {
|
|
10
12
|
options := ctx.Client.OptionsMap()
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
if headers
|
|
14
|
-
|
|
14
|
+
out := map[string]any{}
|
|
15
|
+
if headers := vs.GetProp(options, "headers"); headers != nil {
|
|
16
|
+
if om, ok := vs.Clone(headers).(map[string]any); ok {
|
|
17
|
+
out = om
|
|
18
|
+
}
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
// A header parameter travels as a header, under the name the definition
|
|
22
|
+
// gives it, and only from this call's own arguments. It replaces a default
|
|
23
|
+
// of the same name, whatever its case.
|
|
24
|
+
if hl, ok := vs.GetPath(ctx.Point, []any{"args", "header"}).([]any); ok {
|
|
25
|
+
for _, hd := range hl {
|
|
26
|
+
name, _ := vs.GetProp(hd, "name").(string)
|
|
27
|
+
orig, _ := vs.GetProp(hd, "orig").(string)
|
|
28
|
+
if "" == name {
|
|
29
|
+
continue
|
|
30
|
+
}
|
|
31
|
+
if "" == orig {
|
|
32
|
+
orig = name
|
|
33
|
+
}
|
|
34
|
+
val := vs.GetProp(ctx.Reqmatch, name)
|
|
35
|
+
if val == nil {
|
|
36
|
+
val = vs.GetProp(ctx.Reqdata, name)
|
|
37
|
+
}
|
|
38
|
+
if val != nil {
|
|
39
|
+
wire := strings.ToLower(orig)
|
|
40
|
+
for key := range out {
|
|
41
|
+
if strings.ToLower(key) == wire {
|
|
42
|
+
delete(out, key)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
out[wire] = vs.Stringify(val)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
20
48
|
}
|
|
21
|
-
|
|
49
|
+
|
|
50
|
+
return out
|
|
22
51
|
}
|
|
@@ -37,6 +37,17 @@ func prepareQueryUtil(ctx *core.Context) map[string]any {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
// A header parameter travels in the headers, which prepareHeaders fills.
|
|
41
|
+
if point != nil {
|
|
42
|
+
if hl, ok := vs.GetPath(point, []any{"args", "header"}).([]any); ok {
|
|
43
|
+
for _, hd := range hl {
|
|
44
|
+
if name, ok := vs.GetProp(hd, "name").(string); ok {
|
|
45
|
+
params = append(params, name)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
40
51
|
// A query parameter travels under the name the definition gives it, its
|
|
41
52
|
// orig, which the model may have renamed for the caller.
|
|
42
53
|
wire := map[string]string{}
|
|
@@ -14,18 +14,20 @@ func transformRequestUtil(ctx *core.Context) any {
|
|
|
14
14
|
spec.Step = "reqform"
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
data := omitKeys(ctx.Reqdata, headerArgNames(point))
|
|
18
|
+
|
|
17
19
|
transform := core.ToMapAny(vs.GetProp(point, "transform"))
|
|
18
20
|
if transform == nil {
|
|
19
|
-
return stripAction(
|
|
21
|
+
return stripAction(data)
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
reqform := vs.GetProp(transform, "req")
|
|
23
25
|
if reqform == nil {
|
|
24
|
-
return stripAction(
|
|
26
|
+
return stripAction(data)
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
reqdata, terr := vs.Transform(map[string]any{
|
|
28
|
-
"reqdata":
|
|
30
|
+
"reqdata": data,
|
|
29
31
|
}, reqform)
|
|
30
32
|
|
|
31
33
|
if terr != nil {
|
|
@@ -42,16 +44,40 @@ func transformRequestUtil(ctx *core.Context) any {
|
|
|
42
44
|
// `$action` selects the point (see makePointUtil); it is never an API field,
|
|
43
45
|
// so the body is a copy without it. The caller's map is left untouched.
|
|
44
46
|
func stripAction(reqdata any) any {
|
|
47
|
+
return omitKeys(reqdata, []string{"$action"})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// A header argument travels as a header, which prepareHeadersUtil sends, so
|
|
51
|
+
// the body is built from the request data without it.
|
|
52
|
+
func headerArgNames(point any) []string {
|
|
53
|
+
names := []string{}
|
|
54
|
+
if hl, ok := vs.GetPath(point, []any{"args", "header"}).([]any); ok {
|
|
55
|
+
for _, hd := range hl {
|
|
56
|
+
if name, _ := vs.GetProp(hd, "name").(string); "" != name {
|
|
57
|
+
names = append(names, name)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return names
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
func omitKeys(reqdata any, names []string) any {
|
|
45
65
|
src, ok := reqdata.(map[string]any)
|
|
46
66
|
if !ok {
|
|
47
67
|
return reqdata
|
|
48
68
|
}
|
|
49
|
-
|
|
69
|
+
skip := map[string]bool{}
|
|
70
|
+
for _, name := range names {
|
|
71
|
+
if _, has := src[name]; has {
|
|
72
|
+
skip[name] = true
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if 0 == len(skip) {
|
|
50
76
|
return reqdata
|
|
51
77
|
}
|
|
52
78
|
body := make(map[string]any, len(src))
|
|
53
79
|
for k, v := range src {
|
|
54
|
-
if k
|
|
80
|
+
if !skip[k] {
|
|
55
81
|
body[k] = v
|
|
56
82
|
}
|
|
57
83
|
}
|
|
@@ -34,6 +34,14 @@ final class MakeUrl {
|
|
|
34
34
|
joinParts.add(spec.suffix);
|
|
35
35
|
String url = Struct.join(joinParts, "/", true);
|
|
36
36
|
|
|
37
|
+
// A route the definition ends with a slash keeps it: a server such as a
|
|
38
|
+
// Django REST one redirects or refuses the route without it.
|
|
39
|
+
Object orig = ctx.point == null ? null : Struct.getprop(ctx.point, "orig", null);
|
|
40
|
+
if (orig instanceof String && ((String) orig).endsWith("/") &&
|
|
41
|
+
(spec.suffix == null || spec.suffix.isEmpty()) && !url.endsWith("/")) {
|
|
42
|
+
url = url + "/";
|
|
43
|
+
}
|
|
44
|
+
|
|
37
45
|
Map<String, Object> resmatch = new LinkedHashMap<>();
|
|
38
46
|
|
|
39
47
|
Map<String, Object> params = spec.params;
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
package JAVAPACKAGE.utility;
|
|
2
2
|
|
|
3
3
|
import java.util.LinkedHashMap;
|
|
4
|
+
import java.util.List;
|
|
5
|
+
import java.util.Locale;
|
|
4
6
|
import java.util.Map;
|
|
5
7
|
|
|
6
8
|
import JAVAPACKAGE.core.Context;
|
|
7
9
|
import JAVAPACKAGE.core.Helpers;
|
|
8
10
|
import JAVAPACKAGE.utility.struct.Struct;
|
|
9
11
|
|
|
12
|
+
@SuppressWarnings({"unchecked"})
|
|
10
13
|
final class PrepareHeaders {
|
|
11
14
|
|
|
12
15
|
private PrepareHeaders() {}
|
|
@@ -15,14 +18,35 @@ final class PrepareHeaders {
|
|
|
15
18
|
Map<String, Object> options = ctx.client.optionsMap();
|
|
16
19
|
|
|
17
20
|
Object headers = Struct.getprop(options, "headers");
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
Map<String, Object> out = headers == null ? null : Helpers.toMapAny(Struct.clone(headers));
|
|
22
|
+
if (out == null) {
|
|
23
|
+
out = new LinkedHashMap<>();
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
// A header parameter travels as a header, under the name the definition
|
|
27
|
+
// gives it, and only from this call's own arguments. It replaces a default
|
|
28
|
+
// of the same name, whatever its case.
|
|
29
|
+
Object hl = ctx.point == null ? null : Struct.getpath(ctx.point, List.of("args", "header"));
|
|
30
|
+
if (hl instanceof List) {
|
|
31
|
+
for (Object hd : (List<Object>) hl) {
|
|
32
|
+
Object name = Struct.getprop(hd, "name");
|
|
33
|
+
if (!(name instanceof String) || ((String) name).isEmpty()) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
Object orig = Struct.getprop(hd, "orig");
|
|
37
|
+
String wire = orig instanceof String && !((String) orig).isEmpty() ? (String) orig : (String) name;
|
|
38
|
+
Object val = ctx.reqmatch == null ? null : Struct.getprop(ctx.reqmatch, name, null);
|
|
39
|
+
if (val == null && ctx.reqdata != null) {
|
|
40
|
+
val = Struct.getprop(ctx.reqdata, name, null);
|
|
41
|
+
}
|
|
42
|
+
if (val != null) {
|
|
43
|
+
String key = wire.toLowerCase(Locale.ROOT);
|
|
44
|
+
out.keySet().removeIf(k -> k != null && k.toLowerCase(Locale.ROOT).equals(key));
|
|
45
|
+
out.put(key, Struct.stringify(val));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
25
48
|
}
|
|
26
|
-
|
|
49
|
+
|
|
50
|
+
return out;
|
|
27
51
|
}
|
|
28
52
|
}
|
|
@@ -37,6 +37,16 @@ final class PrepareQuery {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
+
// A header parameter travels in the headers, which prepareHeaders fills.
|
|
41
|
+
Object hl = Struct.getpath(point, List.of("args", "header"));
|
|
42
|
+
if (hl instanceof List) {
|
|
43
|
+
for (Object hd : (List<Object>) hl) {
|
|
44
|
+
Object name = Struct.getprop(hd, "name");
|
|
45
|
+
if (name instanceof String) {
|
|
46
|
+
params.add(name);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
40
50
|
}
|
|
41
51
|
|
|
42
52
|
// A query parameter travels under the name the definition gives it, its
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
package JAVAPACKAGE.utility;
|
|
2
2
|
|
|
3
|
+
import java.util.ArrayList;
|
|
3
4
|
import java.util.LinkedHashMap;
|
|
5
|
+
import java.util.List;
|
|
4
6
|
import java.util.Map;
|
|
5
7
|
|
|
6
8
|
import JAVAPACKAGE.core.Context;
|
|
@@ -16,19 +18,21 @@ final class TransformRequest {
|
|
|
16
18
|
ctx.spec.step = "reqform";
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
Object reqdata = omit(ctx.reqdata, headerArgNames(ctx));
|
|
22
|
+
|
|
19
23
|
Map<String, Object> transform =
|
|
20
24
|
Helpers.toMapAny(Struct.getprop(ctx.point, "transform"));
|
|
21
25
|
if (transform == null) {
|
|
22
|
-
return stripAction(
|
|
26
|
+
return stripAction(reqdata);
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
Object reqform = Struct.getprop(transform, "req", null);
|
|
26
30
|
if (reqform == null) {
|
|
27
|
-
return stripAction(
|
|
31
|
+
return stripAction(reqdata);
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
Map<String, Object> data = new LinkedHashMap<>();
|
|
31
|
-
data.put("reqdata",
|
|
35
|
+
data.put("reqdata", reqdata);
|
|
32
36
|
|
|
33
37
|
return stripAction(Struct.transform(data, reqform));
|
|
34
38
|
}
|
|
@@ -36,12 +40,33 @@ final class TransformRequest {
|
|
|
36
40
|
// `$action` selects the point (see MakePoint); it is never an API field, so
|
|
37
41
|
// the body is a copy without it. The caller's map is left untouched.
|
|
38
42
|
private static Object stripAction(Object reqdata) {
|
|
39
|
-
|
|
43
|
+
return omit(reqdata, List.of("$action"));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// A header argument travels as a header, which PrepareHeaders sends, so the
|
|
47
|
+
// body is built from the request data without it.
|
|
48
|
+
@SuppressWarnings("unchecked")
|
|
49
|
+
private static List<String> headerArgNames(Context ctx) {
|
|
50
|
+
List<String> names = new ArrayList<>();
|
|
51
|
+
Object hl = ctx.point == null ? null : Struct.getpath(ctx.point, List.of("args", "header"));
|
|
52
|
+
if (hl instanceof List) {
|
|
53
|
+
for (Object hd : (List<Object>) hl) {
|
|
54
|
+
Object name = Struct.getprop(hd, "name", null);
|
|
55
|
+
if (name instanceof String && !((String) name).isEmpty()) {
|
|
56
|
+
names.add((String) name);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return names;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private static Object omit(Object reqdata, List<String> names) {
|
|
64
|
+
if (!(reqdata instanceof Map) || names.stream().noneMatch(((Map<?, ?>) reqdata)::containsKey)) {
|
|
40
65
|
return reqdata;
|
|
41
66
|
}
|
|
42
67
|
Map<String, Object> body = new LinkedHashMap<>();
|
|
43
68
|
for (Map.Entry<?, ?> e : ((Map<?, ?>) reqdata).entrySet()) {
|
|
44
|
-
if (!
|
|
69
|
+
if (!names.contains(e.getKey())) {
|
|
45
70
|
body.put(String.valueOf(e.getKey()), e.getValue());
|
|
46
71
|
}
|
|
47
72
|
}
|
|
@@ -7,13 +7,26 @@ class Response {
|
|
|
7
7
|
this.status = getprop(resmap, 'status', -1)
|
|
8
8
|
this.statusText = getprop(resmap, 'statusText', '')
|
|
9
9
|
this.headers = getprop(resmap, 'headers')
|
|
10
|
-
this.json =
|
|
10
|
+
this.json = readJson(resmap)
|
|
11
11
|
this.body = getprop(resmap, 'body')
|
|
12
12
|
this.err = getprop(resmap, 'err')
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
// An empty body, such as the one an accepted delete answers with, is no
|
|
18
|
+
// body: parsing it as JSON would throw after the call has succeeded.
|
|
19
|
+
function readJson(resmap) {
|
|
20
|
+
if ('function' === typeof resmap.text) {
|
|
21
|
+
return async () => {
|
|
22
|
+
const text = await resmap.text()
|
|
23
|
+
return '' === text.trim() ? undefined : JSON.parse(text)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return resmap.json ? resmap.json.bind(resmap) : async () => undefined
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
17
30
|
module.exports = {
|
|
18
31
|
Response,
|
|
19
32
|
}
|
|
@@ -19,6 +19,13 @@ function makeUrl(ctx) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
let url = join([spec.base, spec.prefix, spec.path, spec.suffix], '/', true)
|
|
22
|
+
|
|
23
|
+
// A route the definition ends with a slash keeps it: a server such as a
|
|
24
|
+
// Django REST one redirects or refuses the route without it.
|
|
25
|
+
const orig = ctx.point && ctx.point.orig
|
|
26
|
+
if ('string' === typeof orig && orig.endsWith('/') && !spec.suffix && !url.endsWith('/')) {
|
|
27
|
+
url += '/'
|
|
28
|
+
}
|
|
22
29
|
let resmatch = {}
|
|
23
30
|
|
|
24
31
|
const params = spec.params
|
|
@@ -3,6 +3,7 @@ function prepareHeaders(ctx) {
|
|
|
3
3
|
const struct = ctx.utility.struct
|
|
4
4
|
const clone = struct.clone
|
|
5
5
|
const getprop = struct.getprop
|
|
6
|
+
const stringify = struct.stringify
|
|
6
7
|
|
|
7
8
|
const client = ctx.client
|
|
8
9
|
|
|
@@ -10,6 +11,23 @@ function prepareHeaders(ctx) {
|
|
|
10
11
|
|
|
11
12
|
let out = clone(getprop(options, 'headers', {}))
|
|
12
13
|
|
|
14
|
+
// A header parameter travels as a header, under the name the definition
|
|
15
|
+
// gives it, and only from this call's own arguments. It replaces a default
|
|
16
|
+
// of the same name, whatever its case.
|
|
17
|
+
const hargs = (ctx.point && ctx.point.args && ctx.point.args.header) || []
|
|
18
|
+
for (const h of hargs) {
|
|
19
|
+
if (null == h || 'string' !== typeof h.name || '' === h.name) continue
|
|
20
|
+
let val = getprop(ctx.reqmatch, h.name)
|
|
21
|
+
if (null == val) val = getprop(ctx.reqdata, h.name)
|
|
22
|
+
if (null != val) {
|
|
23
|
+
const wire = String(h.orig || h.name).toLowerCase()
|
|
24
|
+
for (const key of Object.keys(out)) {
|
|
25
|
+
if (wire === key.toLowerCase()) delete out[key]
|
|
26
|
+
}
|
|
27
|
+
out[wire] = stringify(val)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
13
31
|
return out
|
|
14
32
|
}
|
|
15
33
|
|
|
@@ -13,8 +13,10 @@ function prepareQuery(ctx) {
|
|
|
13
13
|
|
|
14
14
|
// A path parameter travels in the path. The generated config lists them as
|
|
15
15
|
// args.params, which prepareParams reads; params is the older list of names.
|
|
16
|
+
// A header parameter travels in the headers, which prepareHeaders fills.
|
|
16
17
|
const inpath = params.concat(
|
|
17
|
-
((point.args && point.args.params) || []).map((p) => p && p.name)
|
|
18
|
+
((point.args && point.args.params) || []).map((p) => p && p.name),
|
|
19
|
+
((point.args && point.args.header) || []).map((h) => h && h.name))
|
|
18
20
|
|
|
19
21
|
// A query parameter travels under the name the definition gives it, its
|
|
20
22
|
// orig, which the model may have renamed for the caller.
|
|
@@ -13,7 +13,7 @@ function transformRequest(ctx) {
|
|
|
13
13
|
try {
|
|
14
14
|
const reqform = point.transform.req
|
|
15
15
|
const reqdata = isfunc(reqform) ? reqform(ctx) : transform({
|
|
16
|
-
reqdata: ctx.reqdata
|
|
16
|
+
reqdata: omit(ctx.reqdata, headerArgNames(ctx))
|
|
17
17
|
}, reqform)
|
|
18
18
|
|
|
19
19
|
return stripAction(reqdata)
|
|
@@ -42,17 +42,31 @@ function transformRequest(ctx) {
|
|
|
42
42
|
// as StructUtility's condenseSet, for the same reason.
|
|
43
43
|
|
|
44
44
|
function stripAction(reqdata) {
|
|
45
|
+
return omit(reqdata, ['$action'])
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
// A header argument travels as a header, which prepareHeaders sends, so the
|
|
50
|
+
// body is built from the request data without it.
|
|
51
|
+
function headerArgNames(ctx) {
|
|
52
|
+
const hargs = (ctx.point && ctx.point.args && ctx.point.args.header) || []
|
|
53
|
+
return hargs.map((h) => h && h.name)
|
|
54
|
+
.filter((name) => 'string' === typeof name && '' !== name)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
function omit(reqdata, names) {
|
|
45
59
|
if (null == reqdata || 'object' !== typeof reqdata || Array.isArray(reqdata)) {
|
|
46
60
|
return reqdata
|
|
47
61
|
}
|
|
48
62
|
|
|
49
|
-
if (!Object.prototype.hasOwnProperty.call(reqdata,
|
|
63
|
+
if (!names.some((name) => Object.prototype.hasOwnProperty.call(reqdata, name))) {
|
|
50
64
|
return reqdata
|
|
51
65
|
}
|
|
52
66
|
|
|
53
67
|
const body = {}
|
|
54
68
|
for (const key of Object.keys(reqdata)) {
|
|
55
|
-
if (
|
|
69
|
+
if (names.includes(key)) {
|
|
56
70
|
continue
|
|
57
71
|
}
|
|
58
72
|
if ('__proto__' === key) {
|