@voxgig/sdkgen 4.31.0 → 4.32.0
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/action/package.d.ts +2 -1
- package/dist/action/package.js +8 -1
- package/dist/action/package.js.map +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/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/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/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/zig/core/utility.zig +80 -20
- package/project/sdkgen-package.json +1 -1
- package/src/action/package.ts +10 -1
- package/src/helpers/definitionPlan.ts +49 -7
|
@@ -2,12 +2,62 @@
|
|
|
2
2
|
|
|
3
3
|
#include "sdk.h"
|
|
4
4
|
|
|
5
|
+
#include <ctype.h>
|
|
6
|
+
#include <stdlib.h>
|
|
7
|
+
#include <string.h>
|
|
8
|
+
|
|
9
|
+
static bool same_name(const char* a, const char* b) {
|
|
10
|
+
for (; *a && *b; a++, b++) {
|
|
11
|
+
if (tolower((unsigned char)*a) != tolower((unsigned char)*b)) return false;
|
|
12
|
+
}
|
|
13
|
+
return *a == *b;
|
|
14
|
+
}
|
|
15
|
+
|
|
5
16
|
voxgig_value* prepare_headers_util(Context* ctx) {
|
|
6
17
|
voxgig_value* options = ctx->client ? sdk_options_map(ctx->client) : ctx->options;
|
|
7
18
|
|
|
8
19
|
voxgig_value* headers = getp(options, "headers");
|
|
9
|
-
|
|
20
|
+
voxgig_value* out = NULL;
|
|
21
|
+
if (!v_is_noval(headers) && !v_is_null(headers)) {
|
|
22
|
+
voxgig_value* cloned = voxgig_clone(headers);
|
|
23
|
+
if (voxgig_is_map(cloned)) out = cloned;
|
|
24
|
+
}
|
|
25
|
+
if (NULL == out) out = voxgig_new_map();
|
|
26
|
+
|
|
27
|
+
// A header parameter travels as a header, under the name the definition
|
|
28
|
+
// gives it, and only from this call's own arguments. It replaces a default
|
|
29
|
+
// of the same name, whatever its case.
|
|
30
|
+
voxgig_value* aheader = getpath2(ctx->point, "args", "header");
|
|
31
|
+
if (voxgig_is_list(aheader)) {
|
|
32
|
+
voxgig_list* hl = voxgig_as_list(aheader);
|
|
33
|
+
for (size_t i = 0; i < hl->len; i++) {
|
|
34
|
+
voxgig_value* name = getp(hl->items[i], "name");
|
|
35
|
+
if (!voxgig_is_string(name) || voxgig_as_string(name)[0] == '\0') continue;
|
|
36
|
+
voxgig_value* orig = getp(hl->items[i], "orig");
|
|
37
|
+
const char* wire = voxgig_is_string(orig) && voxgig_as_string(orig)[0] != '\0'
|
|
38
|
+
? voxgig_as_string(orig) : voxgig_as_string(name);
|
|
39
|
+
|
|
40
|
+
voxgig_value* val = getp(ctx->reqmatch, voxgig_as_string(name));
|
|
41
|
+
if (v_is_noval(val) || v_is_null(val)) val = getp(ctx->reqdata, voxgig_as_string(name));
|
|
42
|
+
if (v_is_noval(val) || v_is_null(val)) continue;
|
|
43
|
+
|
|
44
|
+
size_t n = strlen(wire);
|
|
45
|
+
char* key = (char*)malloc(n + 1);
|
|
46
|
+
for (size_t k = 0; k < n; k++) key[k] = (char)tolower((unsigned char)wire[k]);
|
|
47
|
+
key[n] = '\0';
|
|
48
|
+
voxgig_map* om = voxgig_as_map(out);
|
|
49
|
+
for (size_t j = om->len; j > 0; j--) {
|
|
50
|
+
if (same_name(om->entries[j - 1].key, key)) {
|
|
51
|
+
voxgig_value* dk = voxgig_new_string(om->entries[j - 1].key);
|
|
52
|
+
voxgig_delprop(out, dk);
|
|
53
|
+
voxgig_release(dk);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
char* text = voxgig_stringify(val, -1);
|
|
57
|
+
setp(out, key, voxgig_new_string_take(text, strlen(text)));
|
|
58
|
+
free(key);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
10
61
|
|
|
11
|
-
|
|
12
|
-
return voxgig_is_map(cloned) ? cloned : voxgig_new_map();
|
|
62
|
+
return out;
|
|
13
63
|
}
|
|
@@ -52,6 +52,8 @@ voxgig_value* prepare_query_util(Context* ctx) {
|
|
|
52
52
|
voxgig_value* params = getp(point, "params");
|
|
53
53
|
if (!voxgig_is_list(params)) params = voxgig_new_list();
|
|
54
54
|
voxgig_value* aparams = getpath2(point, "args", "params");
|
|
55
|
+
// A header parameter travels in the headers, which prepare_headers fills.
|
|
56
|
+
voxgig_value* aheader = getpath2(point, "args", "header");
|
|
55
57
|
voxgig_value* aquery = getpath2(point, "args", "query");
|
|
56
58
|
|
|
57
59
|
voxgig_value* out = voxgig_new_map();
|
|
@@ -61,7 +63,8 @@ voxgig_value* prepare_query_util(Context* ctx) {
|
|
|
61
63
|
const char* key = rm->entries[i].key;
|
|
62
64
|
voxgig_value* val = rm->entries[i].value;
|
|
63
65
|
if (!v_is_noval(val) && !v_is_null(val) && strcmp(key, "$action") != 0 &&
|
|
64
|
-
!params_contains(params, key) && !args_params_contains(aparams, key)
|
|
66
|
+
!params_contains(params, key) && !args_params_contains(aparams, key) &&
|
|
67
|
+
!args_params_contains(aheader, key)) {
|
|
65
68
|
setp(out, query_wire_name(aquery, key), v_share(val));
|
|
66
69
|
}
|
|
67
70
|
}
|
|
@@ -4,39 +4,62 @@
|
|
|
4
4
|
|
|
5
5
|
#include <string.h>
|
|
6
6
|
|
|
7
|
+
/* A header argument travels as a header, which prepare_headers_util sends,
|
|
8
|
+
so the body is built from the request data without it. */
|
|
9
|
+
static bool header_arg(voxgig_value* point, const char* key) {
|
|
10
|
+
voxgig_value* aheader = getpath2(point, "args", "header");
|
|
11
|
+
if (!voxgig_is_list(aheader)) return false;
|
|
12
|
+
voxgig_list* hl = voxgig_as_list(aheader);
|
|
13
|
+
for (size_t i = 0; i < hl->len; i++) {
|
|
14
|
+
voxgig_value* name = getp(hl->items[i], "name");
|
|
15
|
+
if (voxgig_is_string(name) && strcmp(voxgig_as_string(name), key) == 0) return true;
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
7
20
|
/* `$action` selects the point (see make_point_util); it is never an API
|
|
8
|
-
field, so the body is a copy without it
|
|
9
|
-
untouched. */
|
|
10
|
-
static
|
|
21
|
+
field, so the body is a copy without it, or, given a point, without its
|
|
22
|
+
header arguments. The caller's map is left untouched. */
|
|
23
|
+
static bool dropped(const char* key, voxgig_value* point) {
|
|
24
|
+
return NULL == point ? strcmp(key, "$action") == 0 : header_arg(point, key);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static voxgig_value* omit_keys(voxgig_value* reqdata, voxgig_value* point) {
|
|
11
28
|
if (!voxgig_is_map(reqdata)) return reqdata;
|
|
12
29
|
voxgig_map* rm = voxgig_as_map(reqdata);
|
|
13
30
|
bool has = false;
|
|
14
31
|
for (size_t i = 0; i < rm->len; i++) {
|
|
15
|
-
if (
|
|
32
|
+
if (dropped(rm->entries[i].key, point)) { has = true; break; }
|
|
16
33
|
}
|
|
17
34
|
if (!has) return reqdata;
|
|
18
35
|
voxgig_value* body = voxgig_new_map();
|
|
19
36
|
for (size_t i = 0; i < rm->len; i++) {
|
|
20
|
-
if (
|
|
37
|
+
if (!dropped(rm->entries[i].key, point)) {
|
|
21
38
|
setp(body, rm->entries[i].key, v_share(rm->entries[i].value));
|
|
22
39
|
}
|
|
23
40
|
}
|
|
24
41
|
return body;
|
|
25
42
|
}
|
|
26
43
|
|
|
44
|
+
static voxgig_value* strip_action(voxgig_value* reqdata) {
|
|
45
|
+
return omit_keys(reqdata, NULL);
|
|
46
|
+
}
|
|
47
|
+
|
|
27
48
|
voxgig_value* transform_request_util(Context* ctx) {
|
|
28
49
|
Spec* spec = ctx->spec;
|
|
29
50
|
voxgig_value* point = ctx->point;
|
|
30
51
|
|
|
31
52
|
if (spec) spec_set_step(spec, "reqform");
|
|
32
53
|
|
|
54
|
+
voxgig_value* data = NULL == point ? ctx->reqdata : omit_keys(ctx->reqdata, point);
|
|
55
|
+
|
|
33
56
|
voxgig_value* transform = to_map(getp(point, "transform"));
|
|
34
|
-
if (v_is_noval(transform)) return strip_action(
|
|
57
|
+
if (v_is_noval(transform)) return strip_action(data);
|
|
35
58
|
|
|
36
59
|
voxgig_value* reqform = getp(transform, "req");
|
|
37
|
-
if (v_is_noval(reqform) || v_is_null(reqform)) return strip_action(
|
|
60
|
+
if (v_is_noval(reqform) || v_is_null(reqform)) return strip_action(data);
|
|
38
61
|
|
|
39
|
-
voxgig_value* store = cmap(1, "reqdata", v_share(
|
|
62
|
+
voxgig_value* store = cmap(1, "reqdata", v_share(data));
|
|
40
63
|
/* Return the transform result verbatim, as the ts reference does. Falling
|
|
41
64
|
back to ctx->reqdata meant a reqform naming a path that does not resolve
|
|
42
65
|
sent the WHOLE request data instead of nothing. */
|
|
@@ -450,9 +450,30 @@
|
|
|
450
450
|
|
|
451
451
|
(defn u-prepare-headers [ctx]
|
|
452
452
|
(let [options (client-options-map (oget ctx :client))
|
|
453
|
-
headers (vs/getprop options "headers")
|
|
454
|
-
|
|
455
|
-
|
|
453
|
+
headers (vs/getprop options "headers")
|
|
454
|
+
out (if (nil? headers) (vs/jm)
|
|
455
|
+
(let [o (vs/clone headers)] (if (vs/ismap o) o (vs/jm))))
|
|
456
|
+
point (oget ctx :point)
|
|
457
|
+
args (when point (vs/getprop point "args"))
|
|
458
|
+
aheader (let [h (when (vs/ismap args) (vs/getprop args "header"))]
|
|
459
|
+
(if (vs/islist h) h (vs/jt)))]
|
|
460
|
+
;; A header parameter travels as a header, under the name the definition
|
|
461
|
+
;; gives it, and only from this call's own arguments. It replaces a default
|
|
462
|
+
;; of the same name, whatever its case.
|
|
463
|
+
(doseq [hd (vec aheader)]
|
|
464
|
+
(let [name (when (vs/ismap hd) (vs/getprop hd "name"))]
|
|
465
|
+
(when (and (string? name) (seq name))
|
|
466
|
+
(let [orig (vs/getprop hd "orig")
|
|
467
|
+
wire (if (and (string? orig) (seq orig)) orig name)
|
|
468
|
+
v (vs/getprop (oget ctx :reqmatch) name)
|
|
469
|
+
v (if (nil? v) (vs/getprop (oget ctx :reqdata) name) v)]
|
|
470
|
+
(when (some? v)
|
|
471
|
+
(let [key (str/lower-case wire)]
|
|
472
|
+
(doseq [k (vec (.keySet ^java.util.Map out))]
|
|
473
|
+
(when (and (string? k) (= key (str/lower-case k)))
|
|
474
|
+
(.remove ^java.util.Map out k)))
|
|
475
|
+
(.put ^java.util.Map out key (vs/stringify v))))))))
|
|
476
|
+
out))
|
|
456
477
|
|
|
457
478
|
(defn u-param [ctx paramdef]
|
|
458
479
|
(let [point (oget ctx :point)
|
|
@@ -506,8 +527,14 @@
|
|
|
506
527
|
aparams (let [args (when point (vs/getprop point "args"))
|
|
507
528
|
p (when (vs/ismap args) (vs/getprop args "params"))]
|
|
508
529
|
(if (vs/islist p) p (vs/jt)))
|
|
509
|
-
|
|
510
|
-
|
|
530
|
+
;; A header parameter travels in the headers, which u-prepare-headers
|
|
531
|
+
;; fills.
|
|
532
|
+
aheader (let [args (when point (vs/getprop point "args"))
|
|
533
|
+
h (when (vs/ismap args) (vs/getprop args "header"))]
|
|
534
|
+
(if (vs/islist h) h (vs/jt)))
|
|
535
|
+
pset (into (into (into #{} (vec params))
|
|
536
|
+
(keep (fn [pd] (when (vs/ismap pd) (vs/getprop pd "name"))) (vec aparams)))
|
|
537
|
+
(keep (fn [hd] (when (vs/ismap hd) (vs/getprop hd "name"))) (vec aheader)))
|
|
511
538
|
;; A query parameter travels under the name the definition gives it,
|
|
512
539
|
;; its orig, which the model may have renamed for the caller.
|
|
513
540
|
aquery (let [args (when point (vs/getprop point "args"))
|
|
@@ -526,26 +553,39 @@
|
|
|
526
553
|
(.put ^java.util.Map out (get wire k k) v))))
|
|
527
554
|
out))
|
|
528
555
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
(defn- strip-action [reqdata]
|
|
532
|
-
(if (and (vs/ismap reqdata) (.containsKey ^java.util.Map reqdata "$action"))
|
|
556
|
+
(defn- omit-keys [reqdata names]
|
|
557
|
+
(if (and (vs/ismap reqdata) (some #(.containsKey ^java.util.Map reqdata %) names))
|
|
533
558
|
(let [body (vs/jm)]
|
|
534
559
|
(doseq [item (or (vs/items reqdata) [])]
|
|
535
560
|
(let [k (vs/getprop item 0) v (vs/getprop item 1)]
|
|
536
|
-
(when (
|
|
561
|
+
(when-not (some #(= k %) names) (.put ^java.util.Map body k v))))
|
|
537
562
|
body)
|
|
538
563
|
reqdata))
|
|
539
564
|
|
|
565
|
+
;; `$action` selects the point (see u-make-point); it is never an API field,
|
|
566
|
+
;; so the body is a copy without it. The caller's map is left untouched.
|
|
567
|
+
(defn- strip-action [reqdata] (omit-keys reqdata ["$action"]))
|
|
568
|
+
|
|
569
|
+
;; A header argument travels as a header, which u-prepare-headers sends, so the
|
|
570
|
+
;; body is built from the request data without it.
|
|
571
|
+
(defn- header-arg-names [point]
|
|
572
|
+
(let [args (when point (vs/getprop point "args"))
|
|
573
|
+
h (when (vs/ismap args) (vs/getprop args "header"))]
|
|
574
|
+
(if (vs/islist h)
|
|
575
|
+
(filterv #(and (string? %) (seq %))
|
|
576
|
+
(map #(when (vs/ismap %) (vs/getprop % "name")) (vec h)))
|
|
577
|
+
[])))
|
|
578
|
+
|
|
540
579
|
(defn u-transform-request [ctx]
|
|
541
|
-
(let [spec (oget ctx :spec) point (oget ctx :point)
|
|
580
|
+
(let [spec (oget ctx :spec) point (oget ctx :point)
|
|
581
|
+
data (omit-keys (oget ctx :reqdata) (header-arg-names point))]
|
|
542
582
|
(when spec (oset! spec :step "reqform"))
|
|
543
583
|
(let [transform (to-map (vs/getprop point "transform"))]
|
|
544
584
|
(strip-action
|
|
545
|
-
(if (nil? transform)
|
|
585
|
+
(if (nil? transform) data
|
|
546
586
|
(let [reqform (vs/getprop transform "req")]
|
|
547
|
-
(if (nil? reqform)
|
|
548
|
-
(vs/transform (vs/jm "reqdata"
|
|
587
|
+
(if (nil? reqform) data
|
|
588
|
+
(vs/transform (vs/jm "reqdata" data) reqform))))))))
|
|
549
589
|
|
|
550
590
|
(defn u-prepare-body [ctx]
|
|
551
591
|
(if (= "data" (op-input (oget ctx :op))) (ucall ctx :transform-request) nil))
|
|
@@ -817,7 +857,14 @@
|
|
|
817
857
|
(nil? result) ["" (ctx-error ctx "url_no_result" "Expected context result property to be defined.")]
|
|
818
858
|
:else
|
|
819
859
|
(let [url (atom (vs/join (vs/jt (oget spec :base) (oget spec :prefix) (oget spec :path) (oget spec :suffix)) "/" true))
|
|
820
|
-
resmatch (vs/jm)
|
|
860
|
+
resmatch (vs/jm)
|
|
861
|
+
point (oget ctx :point)
|
|
862
|
+
orig (when point (vs/getprop point "orig"))]
|
|
863
|
+
;; A route the definition ends with a slash keeps it: a server such as a
|
|
864
|
+
;; Django REST one redirects or refuses the route without it.
|
|
865
|
+
(when (and (string? orig) (str/ends-with? orig "/") (empty? (str (oget spec :suffix)))
|
|
866
|
+
(not (str/ends-with? @url "/")))
|
|
867
|
+
(swap! url str "/"))
|
|
821
868
|
(doseq [item (or (vs/items (oget spec :params)) [])]
|
|
822
869
|
(let [k (vs/getprop item 0) v (vs/getprop item 1)]
|
|
823
870
|
(when (and (some? v) (string? k))
|
|
@@ -281,6 +281,14 @@ inline std::string makeUrl(CtxPtr ctx) {
|
|
|
281
281
|
Value joinParts = vlist({Value(spec->base), Value(spec->prefix), Value(spec->path), Value(spec->suffix)});
|
|
282
282
|
std::string url = Struct::join(joinParts, "/", true);
|
|
283
283
|
|
|
284
|
+
// A route the definition ends with a slash keeps it: a server such as a
|
|
285
|
+
// Django REST one redirects or refuses the route without it.
|
|
286
|
+
Value orig = ctx->point.is_map() ? getp(ctx->point, "orig") : Value::undef();
|
|
287
|
+
if (orig.is_string() && !orig.as_string().empty() && '/' == orig.as_string().back() &&
|
|
288
|
+
spec->suffix.empty() && (url.empty() || '/' != url.back())) {
|
|
289
|
+
url += "/";
|
|
290
|
+
}
|
|
291
|
+
|
|
284
292
|
Value resmatch = vmap();
|
|
285
293
|
|
|
286
294
|
for (const auto& item : Struct::items(spec->params)) {
|
|
@@ -921,9 +929,35 @@ inline Value prepareBody(CtxPtr ctx) {
|
|
|
921
929
|
inline Value prepareHeaders(CtxPtr ctx) {
|
|
922
930
|
Value options = ctx->client->optionsMap();
|
|
923
931
|
Value headers = getp(options, "headers");
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
932
|
+
Value out = is_nullish(headers) ? vmap() : Helpers::toMapAny(Struct::clone(headers));
|
|
933
|
+
if (!out.is_map()) out = vmap();
|
|
934
|
+
|
|
935
|
+
// A header parameter travels as a header, under the name the definition
|
|
936
|
+
// gives it, and only from this call's own arguments. It replaces a default
|
|
937
|
+
// of the same name, whatever its case.
|
|
938
|
+
auto lower = [](std::string s) {
|
|
939
|
+
for (auto& ch : s) ch = (char)std::tolower((unsigned char)ch);
|
|
940
|
+
return s;
|
|
941
|
+
};
|
|
942
|
+
Value aheader = ctx->point.is_map() ? getp(getp(ctx->point, "args"), "header") : Value::undef();
|
|
943
|
+
if (aheader.is_list()) {
|
|
944
|
+
for (const auto& hd : *aheader.as_list()) {
|
|
945
|
+
Value name = getp(hd, "name");
|
|
946
|
+
if (!name.is_string() || name.as_string().empty()) continue;
|
|
947
|
+
Value orig = getp(hd, "orig");
|
|
948
|
+
std::string wire = lower(orig.is_string() && !orig.as_string().empty() ?
|
|
949
|
+
orig.as_string() : name.as_string());
|
|
950
|
+
Value val = getp(ctx->reqmatch, name.as_string(), Value(nullptr));
|
|
951
|
+
if (val.is_null()) val = getp(ctx->reqdata, name.as_string(), Value(nullptr));
|
|
952
|
+
if (is_nullish(val)) continue;
|
|
953
|
+
for (const auto& item : Struct::items(out)) {
|
|
954
|
+
std::string key = as_str(pair_key(item));
|
|
955
|
+
if (lower(key) == wire) out.as_map()->erase(key);
|
|
956
|
+
}
|
|
957
|
+
map_put(out, wire, Value(Struct::stringify(val)));
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
return out;
|
|
927
961
|
}
|
|
928
962
|
|
|
929
963
|
// ---- param ------------------------------------------------------------
|
|
@@ -1024,10 +1058,12 @@ inline Value prepareQuery(CtxPtr ctx) {
|
|
|
1024
1058
|
|
|
1025
1059
|
// A path parameter travels in the path. The generated config lists them as
|
|
1026
1060
|
// args.params, which prepareParams reads; params is the older list of names.
|
|
1061
|
+
// A header parameter travels in the headers, which prepareHeaders fills.
|
|
1027
1062
|
Value aparams = point.is_map() ? getp(getp(point, "args"), "params") : Value::undef();
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1063
|
+
Value aheader = point.is_map() ? getp(getp(point, "args"), "header") : Value::undef();
|
|
1064
|
+
auto named = [&](const Value& defs, const std::string& s) {
|
|
1065
|
+
if (!defs.is_list()) return false;
|
|
1066
|
+
for (const auto& pd : *defs.as_list()) {
|
|
1031
1067
|
Value name = getp(pd, "name");
|
|
1032
1068
|
if (name.is_string() && name.as_string() == s) return true;
|
|
1033
1069
|
}
|
|
@@ -1055,7 +1091,8 @@ inline Value prepareQuery(CtxPtr ctx) {
|
|
|
1055
1091
|
for (const auto& item : Struct::items(reqmatch)) {
|
|
1056
1092
|
std::string key = as_str(pair_key(item));
|
|
1057
1093
|
Value val = pair_val(item);
|
|
1058
|
-
if (!is_nullish(val) && "$action" != key && !contains_str(params, key) &&
|
|
1094
|
+
if (!is_nullish(val) && "$action" != key && !contains_str(params, key) &&
|
|
1095
|
+
!named(aparams, key) && !named(aheader, key)) {
|
|
1059
1096
|
map_put(out, wire_name(key), val);
|
|
1060
1097
|
}
|
|
1061
1098
|
}
|
|
@@ -1079,29 +1116,49 @@ inline std::string preparePath(CtxPtr ctx) {
|
|
|
1079
1116
|
|
|
1080
1117
|
// ---- transformRequest -------------------------------------------------
|
|
1081
1118
|
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
if (!
|
|
1119
|
+
inline Value omitKeys(const Value& reqdata, const std::vector<std::string>& names) {
|
|
1120
|
+
bool has = false;
|
|
1121
|
+
for (const auto& name : names) has = has || map_contains(reqdata, name);
|
|
1122
|
+
if (!has) return reqdata;
|
|
1086
1123
|
Value body = vmap();
|
|
1087
1124
|
for (const auto& item : Struct::items(reqdata)) {
|
|
1088
1125
|
std::string key = as_str(pair_key(item));
|
|
1089
|
-
if (
|
|
1126
|
+
if (std::find(names.begin(), names.end(), key) == names.end()) map_put(body, key, pair_val(item));
|
|
1090
1127
|
}
|
|
1091
1128
|
return body;
|
|
1092
1129
|
}
|
|
1093
1130
|
|
|
1131
|
+
// `$action` selects the point (see makePoint); it is never an API field, so
|
|
1132
|
+
// the body is a copy without it. The caller's map is left untouched.
|
|
1133
|
+
inline Value stripAction(const Value& reqdata) { return omitKeys(reqdata, {"$action"}); }
|
|
1134
|
+
|
|
1135
|
+
// A header argument travels as a header, which prepareHeaders sends, so the
|
|
1136
|
+
// body is built from the request data without it.
|
|
1137
|
+
inline std::vector<std::string> headerArgNames(CtxPtr ctx) {
|
|
1138
|
+
std::vector<std::string> names;
|
|
1139
|
+
Value aheader = ctx->point.is_map() ? getp(getp(ctx->point, "args"), "header") : Value::undef();
|
|
1140
|
+
if (aheader.is_list()) {
|
|
1141
|
+
for (const auto& hd : *aheader.as_list()) {
|
|
1142
|
+
Value name = getp(hd, "name");
|
|
1143
|
+
if (name.is_string() && !name.as_string().empty()) names.push_back(name.as_string());
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
return names;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1094
1149
|
inline Value transformRequest(CtxPtr ctx) {
|
|
1095
1150
|
if (ctx->spec) ctx->spec->step = "reqform";
|
|
1096
1151
|
|
|
1152
|
+
Value reqdata = omitKeys(ctx->reqdata, headerArgNames(ctx));
|
|
1153
|
+
|
|
1097
1154
|
Value transform = Helpers::toMapAny(getp(ctx->point, "transform"));
|
|
1098
|
-
if (!transform.is_map()) return stripAction(
|
|
1155
|
+
if (!transform.is_map()) return stripAction(reqdata);
|
|
1099
1156
|
|
|
1100
1157
|
Value reqform = getp(transform, "req");
|
|
1101
|
-
if (is_nullish(reqform)) return stripAction(
|
|
1158
|
+
if (is_nullish(reqform)) return stripAction(reqdata);
|
|
1102
1159
|
|
|
1103
1160
|
Value data = vmap();
|
|
1104
|
-
map_put(data, "reqdata",
|
|
1161
|
+
map_put(data, "reqdata", reqdata);
|
|
1105
1162
|
return stripAction(Struct::transform(data, reqform));
|
|
1106
1163
|
}
|
|
1107
1164
|
|
|
@@ -20,6 +20,14 @@ public static partial class SdkUtility
|
|
|
20
20
|
StructUtils.Jt(spec.Base, spec.Prefix, spec.Path, spec.Suffix), "/", true);
|
|
21
21
|
var resmatch = new Dictionary<string, object?>();
|
|
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
|
+
if (ctx.Point != null && StructUtils.GetProp(ctx.Point, "orig") is string orig &&
|
|
26
|
+
orig.EndsWith("/") && string.IsNullOrEmpty(spec.Suffix) && !url.EndsWith("/"))
|
|
27
|
+
{
|
|
28
|
+
url += "/";
|
|
29
|
+
}
|
|
30
|
+
|
|
23
31
|
foreach (var item in StructUtils.Items(spec.Params))
|
|
24
32
|
{
|
|
25
33
|
var key = item[0] as string ?? "";
|
|
@@ -11,12 +11,42 @@ public static partial class SdkUtility
|
|
|
11
11
|
var options = ctx.Client!.OptionsMap();
|
|
12
12
|
|
|
13
13
|
var headers = StructUtils.GetProp(options, "headers");
|
|
14
|
-
|
|
14
|
+
var result = (headers == null ? null : StructUtils.Clone(headers) as Dictionary<string, object?>)
|
|
15
|
+
?? new Dictionary<string, object?>();
|
|
16
|
+
|
|
17
|
+
// A header parameter travels as a header, under the name the
|
|
18
|
+
// definition gives it, and only from this call's own arguments. It
|
|
19
|
+
// replaces a default of the same name, whatever its case.
|
|
20
|
+
if (ctx.Point != null &&
|
|
21
|
+
StructUtils.GetPath(ctx.Point, StructUtils.Jt("args", "header")) is List<object?> hl)
|
|
15
22
|
{
|
|
16
|
-
|
|
23
|
+
foreach (var hd in hl)
|
|
24
|
+
{
|
|
25
|
+
if (StructUtils.GetProp(hd, "name") is not string name || name == "")
|
|
26
|
+
{
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
var wire = StructUtils.GetProp(hd, "orig") is string orig && orig != "" ? orig : name;
|
|
30
|
+
var val = ctx.Reqmatch == null ? null : StructUtils.GetProp(ctx.Reqmatch, name);
|
|
31
|
+
if (val == null && ctx.Reqdata != null)
|
|
32
|
+
{
|
|
33
|
+
val = StructUtils.GetProp(ctx.Reqdata, name);
|
|
34
|
+
}
|
|
35
|
+
if (val != null)
|
|
36
|
+
{
|
|
37
|
+
var key = wire.ToLowerInvariant();
|
|
38
|
+
foreach (var k in new List<string>(result.Keys))
|
|
39
|
+
{
|
|
40
|
+
if (k.ToLowerInvariant() == key)
|
|
41
|
+
{
|
|
42
|
+
result.Remove(k);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
result[key] = StructUtils.Stringify(val);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
17
48
|
}
|
|
18
49
|
|
|
19
|
-
return
|
|
20
|
-
?? new Dictionary<string, object?>();
|
|
50
|
+
return result;
|
|
21
51
|
}
|
|
22
52
|
}
|
|
@@ -31,6 +31,20 @@ public static partial class SdkUtility
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// A header parameter travels in the headers, which PrepareHeaders
|
|
35
|
+
// fills.
|
|
36
|
+
if (point != null &&
|
|
37
|
+
StructUtils.GetPath(point, StructUtils.Jt("args", "header")) is List<object?> ahl)
|
|
38
|
+
{
|
|
39
|
+
foreach (var hd in ahl)
|
|
40
|
+
{
|
|
41
|
+
if (StructUtils.GetProp(hd, "name") is string hname)
|
|
42
|
+
{
|
|
43
|
+
paramnames.Add(hname);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
34
48
|
// A query parameter travels under the name the definition gives it,
|
|
35
49
|
// its orig, which the model may have renamed for the caller.
|
|
36
50
|
var wire = new Dictionary<string, string>();
|
|
@@ -17,21 +17,23 @@ public static partial class SdkUtility
|
|
|
17
17
|
spec.Step = "reqform";
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
var data = OmitKeys(ctx.Reqdata, HeaderArgNames(point));
|
|
21
|
+
|
|
20
22
|
var transform = Helpers.ToMapAny(StructUtils.GetProp(point, "transform"));
|
|
21
23
|
if (transform == null)
|
|
22
24
|
{
|
|
23
|
-
return StripAction(
|
|
25
|
+
return StripAction(data);
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
var reqform = StructUtils.GetProp(transform, "req");
|
|
27
29
|
if (reqform == null)
|
|
28
30
|
{
|
|
29
|
-
return StripAction(
|
|
31
|
+
return StripAction(data);
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
var reqdata = StructUtils.Transform(new Dictionary<string, object?>
|
|
33
35
|
{
|
|
34
|
-
["reqdata"] =
|
|
36
|
+
["reqdata"] = data,
|
|
35
37
|
}, reqform);
|
|
36
38
|
|
|
37
39
|
return StripAction(reqdata);
|
|
@@ -42,14 +44,38 @@ public static partial class SdkUtility
|
|
|
42
44
|
// untouched.
|
|
43
45
|
private static object? StripAction(object? reqdata)
|
|
44
46
|
{
|
|
45
|
-
|
|
47
|
+
return OmitKeys(reqdata, new List<string> { "$action" });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// A header argument travels as a header, which PrepareHeadersUtil sends,
|
|
51
|
+
// so the body is built from the request data without it.
|
|
52
|
+
private static List<string> HeaderArgNames(object? point)
|
|
53
|
+
{
|
|
54
|
+
var names = new List<string>();
|
|
55
|
+
if (point != null &&
|
|
56
|
+
StructUtils.GetPath(point, StructUtils.Jt("args", "header")) is List<object?> hl)
|
|
57
|
+
{
|
|
58
|
+
foreach (var hd in hl)
|
|
59
|
+
{
|
|
60
|
+
if (StructUtils.GetProp(hd, "name") is string name && name != "")
|
|
61
|
+
{
|
|
62
|
+
names.Add(name);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return names;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private static object? OmitKeys(object? reqdata, List<string> names)
|
|
70
|
+
{
|
|
71
|
+
if (reqdata is not IDictionary<string, object?> src || !names.Exists(src.ContainsKey))
|
|
46
72
|
{
|
|
47
73
|
return reqdata;
|
|
48
74
|
}
|
|
49
75
|
var body = new Dictionary<string, object?>();
|
|
50
76
|
foreach (var kv in src)
|
|
51
77
|
{
|
|
52
|
-
if (
|
|
78
|
+
if (!names.Contains(kv.Key))
|
|
53
79
|
{
|
|
54
80
|
body[kv.Key] = kv.Value;
|
|
55
81
|
}
|