@voxgig/sdkgen 4.23.0 → 4.24.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 +18 -2
- package/dist/action/doctor.d.ts +1 -1
- package/dist/action/doctor.js +12 -10
- package/dist/action/doctor.js.map +1 -1
- package/dist/action/edition.js +4 -0
- package/dist/action/edition.js.map +1 -1
- package/dist/action/feature.js +5 -1
- package/dist/action/feature.js.map +1 -1
- package/dist/action/remove.d.ts +16 -0
- package/dist/action/remove.js +345 -0
- package/dist/action/remove.js.map +1 -0
- package/dist/action/target.js +6 -1
- package/dist/action/target.js.map +1 -1
- package/dist/cmp/Deploy.js +9 -6
- package/dist/cmp/Deploy.js.map +1 -1
- package/dist/helpers/semver.js +3 -1
- package/dist/helpers/semver.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/project/.sdk/tm/c/core/context.c +3 -1
- package/project/.sdk/tm/c/feature/paging.c +37 -3
- package/project/.sdk/tm/c/feature/test.c +51 -27
- package/project/.sdk/tm/c/utility/prepare_query.c +2 -1
- package/project/.sdk/tm/c/utility/transform_request.c +25 -3
- package/project/.sdk/tm/clojure/src/sdk/core.clj +21 -7
- package/project/.sdk/tm/clojure/src/sdk/features.clj +68 -18
- package/project/.sdk/tm/cpp/core/types.hpp +1 -1
- package/project/.sdk/tm/cpp/feature/paging.hpp +36 -3
- package/project/.sdk/tm/cpp/feature/test.hpp +41 -13
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +16 -4
- package/project/.sdk/tm/csharp/core/Context.cs +1 -1
- package/project/.sdk/tm/csharp/feature/PagingFeature.cs +58 -4
- package/project/.sdk/tm/csharp/feature/TestFeature.cs +13 -21
- package/project/.sdk/tm/csharp/utility/Fetcher.cs +19 -12
- package/project/.sdk/tm/csharp/utility/PrepareQuery.cs +1 -1
- package/project/.sdk/tm/csharp/utility/TransformRequest.cs +23 -3
- package/project/.sdk/tm/elixir/lib/projectname/context.ex +1 -1
- package/project/.sdk/tm/elixir/lib/projectname/feature/paging.ex +52 -10
- package/project/.sdk/tm/elixir/lib/projectname/feature/test.ex +47 -17
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +30 -9
- package/project/.sdk/tm/go/core/context.go +13 -13
- package/project/.sdk/tm/go/feature/paging_feature.go +40 -2
- package/project/.sdk/tm/go/feature/test_feature.go +3 -15
- package/project/.sdk/tm/go/test/feature_corpus_test.go +1 -0
- package/project/.sdk/tm/go/test/feature_test.go +54 -0
- package/project/.sdk/tm/go/test/pipeline_test.go +163 -57
- package/project/.sdk/tm/go/test/primary_utility_test.go +53 -0
- package/project/.sdk/tm/go/utility/fetcher.go +41 -20
- package/project/.sdk/tm/go/utility/prepare_query.go +1 -1
- package/project/.sdk/tm/go/utility/transform_request.go +22 -3
- package/project/.sdk/tm/java/core/Context.java +2 -1
- package/project/.sdk/tm/java/feature/PagingFeature.java +46 -4
- package/project/.sdk/tm/java/feature/TestFeature.java +10 -13
- package/project/.sdk/tm/java/test/FeatureCorpusTest.java +3 -0
- package/project/.sdk/tm/java/test/FeatureTest.java +42 -0
- package/project/.sdk/tm/java/test/PipelineTest.java +141 -48
- package/project/.sdk/tm/java/test/PrimaryUtilityTest.java +67 -6
- package/project/.sdk/tm/java/utility/Fetcher.java +36 -28
- package/project/.sdk/tm/java/utility/PrepareQuery.java +1 -1
- package/project/.sdk/tm/java/utility/TransformRequest.java +18 -3
- package/project/.sdk/tm/js/src/Context.js +7 -2
- package/project/.sdk/tm/js/src/feature/paging/PagingFeature.js +24 -6
- package/project/.sdk/tm/js/src/feature/test/TestFeature.js +7 -5
- package/project/.sdk/tm/js/src/utility/PrepareQueryUtility.js +1 -1
- package/project/.sdk/tm/js/test/feature/Corpus.test.js +4 -0
- package/project/.sdk/tm/js/test/pipeline.test.js +88 -22
- package/project/.sdk/tm/js/test/utility/PrimaryUtility.test.js +47 -1
- package/project/.sdk/tm/kotlin/core/Context.kt +1 -1
- package/project/.sdk/tm/kotlin/feature/PagingFeature.kt +36 -4
- package/project/.sdk/tm/kotlin/feature/TestFeature.kt +11 -14
- package/project/.sdk/tm/kotlin/utility/Fetcher.kt +33 -24
- package/project/.sdk/tm/kotlin/utility/Prepare.kt +1 -1
- package/project/.sdk/tm/kotlin/utility/ResultTransform.kt +18 -3
- package/project/.sdk/tm/lua/core/context.lua +8 -1
- package/project/.sdk/tm/lua/core/control.lua +3 -0
- package/project/.sdk/tm/lua/feature/paging_feature.lua +44 -5
- package/project/.sdk/tm/lua/feature/test_feature.lua +32 -14
- package/project/.sdk/tm/lua/test/omni.lua +8 -1
- package/project/.sdk/tm/lua/test/pipeline_test.lua +107 -28
- package/project/.sdk/tm/lua/test/primary_utility_test.lua +83 -3
- package/project/.sdk/tm/lua/utility/prepare_query.lua +2 -1
- package/project/.sdk/tm/lua/utility/transform_request.lua +19 -3
- package/project/.sdk/tm/ocaml/sdk_features.ml +72 -21
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +23 -8
- package/project/.sdk/tm/perl/core/context.pm +2 -1
- package/project/.sdk/tm/perl/feature/paging_feature.pm +31 -5
- package/project/.sdk/tm/perl/feature/test_feature.pm +46 -16
- package/project/.sdk/tm/perl/feature/timeout_feature.pm +17 -13
- package/project/.sdk/tm/perl/t/feature_corpus.t +14 -1
- package/project/.sdk/tm/perl/t/omni.pm +7 -1
- package/project/.sdk/tm/perl/t/pipeline.t +124 -27
- package/project/.sdk/tm/perl/t/primary_utility.t +73 -0
- package/project/.sdk/tm/perl/utility/fetcher.pm +15 -1
- package/project/.sdk/tm/perl/utility/prepare_query.pm +1 -0
- package/project/.sdk/tm/perl/utility/transform_request.pm +13 -3
- package/project/.sdk/tm/php/core/Context.php +5 -1
- package/project/.sdk/tm/php/core/Control.php +5 -0
- package/project/.sdk/tm/php/feature/PagingFeature.php +26 -5
- package/project/.sdk/tm/php/feature/TestFeature.php +27 -42
- package/project/.sdk/tm/php/test/FeatureCorpusTest.php +17 -1
- package/project/.sdk/tm/php/test/FeatureTest.php +44 -0
- package/project/.sdk/tm/php/test/PipelineTest.php +143 -26
- package/project/.sdk/tm/php/test/PrimaryUtilityTest.php +80 -2
- package/project/.sdk/tm/php/utility/Fetcher.php +17 -3
- package/project/.sdk/tm/php/utility/PrepareQuery.php +1 -1
- package/project/.sdk/tm/php/utility/TransformRequest.php +15 -3
- package/project/.sdk/tm/py/pkg/core/context.py +2 -1
- package/project/.sdk/tm/py/pkg/feature/paging_feature.py +29 -4
- package/project/.sdk/tm/py/pkg/feature/test_feature.py +29 -12
- package/project/.sdk/tm/py/pkg/utility/fetcher.py +63 -65
- package/project/.sdk/tm/py/pkg/utility/prepare_query.py +2 -1
- package/project/.sdk/tm/py/pkg/utility/transform_request.py +11 -3
- package/project/.sdk/tm/py/test/test_feature.py +27 -0
- package/project/.sdk/tm/py/test/test_feature_corpus.py +11 -1
- package/project/.sdk/tm/py/test/test_pipeline.py +108 -31
- package/project/.sdk/tm/py/test/test_primary_utility.py +76 -1
- package/project/.sdk/tm/rb/core/context.rb +1 -1
- package/project/.sdk/tm/rb/feature/paging_feature.rb +27 -5
- package/project/.sdk/tm/rb/feature/test_feature.rb +33 -7
- package/project/.sdk/tm/rb/test/feature_corpus_test.rb +12 -1
- package/project/.sdk/tm/rb/test/feature_test.rb +38 -0
- package/project/.sdk/tm/rb/test/pipeline_test.rb +100 -24
- package/project/.sdk/tm/rb/test/primary_utility_test.rb +59 -1
- package/project/.sdk/tm/rb/utility/fetcher.rb +49 -3
- package/project/.sdk/tm/rb/utility/prepare_query.rb +1 -1
- package/project/.sdk/tm/rb/utility/transform_request.rb +10 -3
- package/project/.sdk/tm/rust/core/context.rs +2 -6
- package/project/.sdk/tm/rust/feature/paging.rs +44 -3
- package/project/.sdk/tm/rust/feature/secrets.rs +1 -1
- package/project/.sdk/tm/rust/feature/test.rs +66 -33
- package/project/.sdk/tm/rust/utility/fetcher.rs +41 -14
- package/project/.sdk/tm/rust/utility/prepare_query.rs +1 -1
- package/project/.sdk/tm/rust/utility/transform_request.rs +31 -5
- package/project/.sdk/tm/scala/core/Context.scala +2 -1
- package/project/.sdk/tm/scala/feature/PagingFeature.scala +41 -4
- package/project/.sdk/tm/scala/feature/TestFeature.scala +11 -12
- package/project/.sdk/tm/scala/utility/Fetcher.scala +34 -13
- package/project/.sdk/tm/scala/utility/Prepare.scala +1 -1
- package/project/.sdk/tm/scala/utility/Transform.scala +19 -3
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Context.swift +1 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/PagingFeature.swift +31 -4
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/TestFeature.swift +12 -9
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Fetcher.swift +22 -3
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Prepare.swift +1 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/ResultUtil.swift +14 -3
- package/project/.sdk/tm/ts/src/Context.ts +7 -2
- package/project/.sdk/tm/ts/src/feature/paging/PagingFeature.ts +19 -3
- package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +3 -0
- package/project/.sdk/tm/ts/src/utility/PrepareQueryUtility.ts +1 -1
- package/project/.sdk/tm/ts/test/feature/Corpus.test.ts +1 -0
- package/project/.sdk/tm/ts/test/pipeline.test.ts +52 -11
- package/project/.sdk/tm/zig/core/context.zig +1 -1
- package/project/.sdk/tm/zig/core/utility.zig +19 -5
- package/project/.sdk/tm/zig/feature/paging.zig +34 -4
- package/project/.sdk/tm/zig/feature/test.zig +54 -26
- package/project/sdkgen-package.json +1 -1
- package/src/action/doctor.ts +12 -9
- package/src/action/edition.ts +8 -0
- package/src/action/feature.ts +7 -1
- package/src/action/remove.ts +492 -0
- package/src/action/target.ts +8 -1
- package/src/cmp/Deploy.ts +11 -6
- package/src/helpers/semver.ts +3 -1
|
@@ -189,7 +189,9 @@ let make_context_impl (cs : ctxspec) (basectx : ctx option) : ctx =
|
|
|
189
189
|
(match getp cr "actor" with Noval -> () | a -> c.ctrl_actor <- a);
|
|
190
190
|
(match getp cr "paging" with Map _ as m -> c.ctrl_paging <- m | _ -> ());
|
|
191
191
|
c
|
|
192
|
-
| _ -> (match basectx with
|
|
192
|
+
| _ -> (match basectx with
|
|
193
|
+
| Some b when cs.cs_opname = None -> b.c_ctrl
|
|
194
|
+
| _ -> new_control ())
|
|
193
195
|
in
|
|
194
196
|
let meta =
|
|
195
197
|
match cs.cs_meta with Some (Map _ as m) -> m
|
|
@@ -413,7 +415,7 @@ let prepare_query_util (ctx : ctx) : value =
|
|
|
413
415
|
let out = empty_map () in
|
|
414
416
|
List.iter (fun k ->
|
|
415
417
|
let v = getp reqmatch k in
|
|
416
|
-
if not (is_noval v) && not (contains_param k) then setp out k v)
|
|
418
|
+
if not (is_noval v) && k <> "$action" && not (contains_param k) then setp out k v)
|
|
417
419
|
(keysof reqmatch);
|
|
418
420
|
out
|
|
419
421
|
|
|
@@ -555,14 +557,27 @@ let prepare_auth_util = Sdk_prepare_auth.prepare_auth_util
|
|
|
555
557
|
|
|
556
558
|
(* ----- transforms / result helpers ----- *)
|
|
557
559
|
|
|
560
|
+
(* `$action` selects the point (see make_point_util); it is never an API
|
|
561
|
+
field, so the body is a copy without it. The caller's map is left
|
|
562
|
+
untouched. *)
|
|
563
|
+
let strip_action (reqdata : value) : value =
|
|
564
|
+
match reqdata with
|
|
565
|
+
| Map _ when List.mem "$action" (keysof reqdata) ->
|
|
566
|
+
let body = empty_map () in
|
|
567
|
+
List.iter (fun k -> if k <> "$action" then setp body k (getp reqdata k))
|
|
568
|
+
(keysof reqdata);
|
|
569
|
+
body
|
|
570
|
+
| _ -> reqdata
|
|
571
|
+
|
|
558
572
|
let transform_request_util (ctx : ctx) : value =
|
|
559
573
|
(match ctx.c_spec with Some s -> s.sp_step <- "reqform" | None -> ());
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
574
|
+
strip_action
|
|
575
|
+
(match to_map (getp ctx.c_point "transform") with
|
|
576
|
+
| Map _ as tr ->
|
|
577
|
+
(match getp tr "req" with
|
|
578
|
+
| Noval -> ctx.c_reqdata
|
|
579
|
+
| reqform -> transform (jo [("reqdata", ctx.c_reqdata)]) reqform)
|
|
580
|
+
| _ -> ctx.c_reqdata)
|
|
566
581
|
|
|
567
582
|
let transform_response_util (ctx : ctx) : value =
|
|
568
583
|
(match ctx.c_spec with Some s -> s.sp_step <- "resform" | None -> ());
|
|
@@ -45,7 +45,8 @@ sub new {
|
|
|
45
45
|
$self->{ctrl}{actor} = $ctrl_raw->{actor} if exists $ctrl_raw->{actor};
|
|
46
46
|
$self->{ctrl}{paging} = $ctrl_raw->{paging} if Voxgig::Struct::ismap($ctrl_raw->{paging});
|
|
47
47
|
}
|
|
48
|
-
elsif ($basectx && $basectx->{ctrl}
|
|
48
|
+
elsif ($basectx && $basectx->{ctrl}
|
|
49
|
+
&& !defined ProjectNameHelpers::get_ctx_prop($ctxmap, 'opname')) {
|
|
49
50
|
$self->{ctrl} = $basectx->{ctrl};
|
|
50
51
|
}
|
|
51
52
|
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
# stamps page/limit (or a cursor) into the request query; on the way back
|
|
5
5
|
# (PreResult) it reads the server's pagination signals - a Link rel="next"
|
|
6
6
|
# header, X-Page/X-Next-Page/X-Total-Count headers, or next/cursor/
|
|
7
|
-
# nextCursor/hasMore fields in the body - and records
|
|
8
|
-
# result.paging. A per-call ctrl paging value (page or cursor) takes
|
|
7
|
+
# nextCursor/hasMore fields in the body (snake_case forms too) - and records
|
|
8
|
+
# them on result.paging and the caller's ctrl paging. A per-call ctrl paging value (page or cursor) takes
|
|
9
9
|
# priority. Parameter names ("pageParam", "limitParam", "cursorParam"),
|
|
10
10
|
# "startPage" (default 1) and page size ("limit") are configurable.
|
|
11
11
|
|
|
@@ -76,8 +76,11 @@ sub PreRequest {
|
|
|
76
76
|
$spec->{query}{$cursor_param} = $paging->{cursor};
|
|
77
77
|
}
|
|
78
78
|
elsif (!defined $spec->{query}{$page_param}) {
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
# A record written back by PreResult holds the page just fetched as
|
|
80
|
+
# "page" and the one to fetch as "nextPage", so nextPage wins.
|
|
81
|
+
my $page = defined $paging->{nextPage} ? $paging->{nextPage} : $paging->{page};
|
|
82
|
+
$spec->{query}{$page_param} = defined $page
|
|
83
|
+
? $page
|
|
81
84
|
: (defined $self->{options}{startPage} ? $self->{options}{startPage} : 1);
|
|
82
85
|
}
|
|
83
86
|
|
|
@@ -186,15 +189,27 @@ sub PreResult {
|
|
|
186
189
|
}
|
|
187
190
|
}
|
|
188
191
|
|
|
189
|
-
# Body-level
|
|
192
|
+
# Body-level signals; snake_case forms are read first so camelCase wins.
|
|
190
193
|
if (Voxgig::Struct::ismap($body)) {
|
|
191
194
|
my $bnext = ProjectNameHelpers::gp($body, 'next');
|
|
192
195
|
$paging->{next} = defined $paging->{next} ? $paging->{next} : $bnext
|
|
193
196
|
if defined $bnext;
|
|
197
|
+
my $bsnakecursor = ProjectNameHelpers::gp($body, 'next_cursor');
|
|
198
|
+
$paging->{cursor} = $bsnakecursor if defined $bsnakecursor;
|
|
194
199
|
my $bcursor = ProjectNameHelpers::gp($body, 'cursor');
|
|
195
200
|
$paging->{cursor} = $bcursor if defined $bcursor;
|
|
196
201
|
my $bnextcursor = ProjectNameHelpers::gp($body, 'nextCursor');
|
|
197
202
|
$paging->{cursor} = $bnextcursor if defined $bnextcursor;
|
|
203
|
+
unless (defined $paging->{nextPage}) {
|
|
204
|
+
my $np = ProjectNameHelpers::gp($body, 'nextPage');
|
|
205
|
+
$np = ProjectNameHelpers::gp($body, 'next_page') unless defined $np;
|
|
206
|
+
$paging->{nextPage} = $np if defined $np && !ref($np);
|
|
207
|
+
}
|
|
208
|
+
my $bsnakemore = $body->{has_more};
|
|
209
|
+
if (ProjectNameHelpers::is_true($bsnakemore) || ProjectNameHelpers::is_false($bsnakemore)) {
|
|
210
|
+
$paging->{hasMore} = ProjectNameHelpers::is_true($bsnakemore) ? 1 : 0;
|
|
211
|
+
$explicit_more = 1;
|
|
212
|
+
}
|
|
198
213
|
my $bhasmore = $body->{hasMore};
|
|
199
214
|
if (ProjectNameHelpers::is_true($bhasmore) || ProjectNameHelpers::is_false($bhasmore)) {
|
|
200
215
|
$paging->{hasMore} = ProjectNameHelpers::is_true($bhasmore) ? 1 : 0;
|
|
@@ -215,6 +230,17 @@ sub PreResult {
|
|
|
215
230
|
|
|
216
231
|
$result->{paging} = $paging;
|
|
217
232
|
|
|
233
|
+
if ($ctx->{ctrl}) {
|
|
234
|
+
# The Context shares the caller's paging hash by reference, so refill it
|
|
235
|
+
# in place; a fresh hash on ctrl never reaches the caller.
|
|
236
|
+
if (Voxgig::Struct::ismap($ctx->{ctrl}{paging})) {
|
|
237
|
+
%{ $ctx->{ctrl}{paging} } = %$paging;
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
$ctx->{ctrl}{paging} = $paging;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
218
244
|
$self->{client}{_paging} = { 'last' => $paging };
|
|
219
245
|
return;
|
|
220
246
|
}
|
|
@@ -154,18 +154,11 @@ sub init {
|
|
|
154
154
|
my $args = $test_self->build_args($fctx, $op, $update_match);
|
|
155
155
|
my $found = Voxgig::Struct::select($entmap, $args);
|
|
156
156
|
my $ent = ProjectNameHelpers::ge($found, 0);
|
|
157
|
-
|
|
158
|
-
for my $k (sort keys %$entmap) {
|
|
159
|
-
if (Voxgig::Struct::ismap($entmap->{$k})) {
|
|
160
|
-
$ent = $entmap->{$k};
|
|
161
|
-
last;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
157
|
+
# update miss: 404, never another record
|
|
165
158
|
return $respond->($fctx, 404, undef, { 'statusText' => 'Not found' })
|
|
166
159
|
unless ProjectNameHelpers::rb_truthy($ent);
|
|
167
|
-
if (Voxgig::Struct::ismap($ent) && $fctx->{reqdata}) {
|
|
168
|
-
$ent
|
|
160
|
+
if (Voxgig::Struct::ismap($ent) && Voxgig::Struct::ismap($fctx->{reqdata})) {
|
|
161
|
+
Voxgig::Struct::merge([$ent, $fctx->{reqdata}]);
|
|
169
162
|
}
|
|
170
163
|
Voxgig::Struct::delprop($ent, '$KEY');
|
|
171
164
|
my $out = Voxgig::Struct::clone($ent);
|
|
@@ -290,18 +283,55 @@ sub make_netsim {
|
|
|
290
283
|
};
|
|
291
284
|
}
|
|
292
285
|
|
|
286
|
+
sub _point_terminal {
|
|
287
|
+
my ($p) = @_;
|
|
288
|
+
my $parts = ProjectNameHelpers::gp($p, 'parts');
|
|
289
|
+
return 0 unless Voxgig::Struct::islist($parts) && @$parts;
|
|
290
|
+
my $last = $parts->[-1];
|
|
291
|
+
return (defined $last && !ref $last && 0 == index($last, '{')) ? 1 : 0;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
sub _point_depth {
|
|
295
|
+
my ($p) = @_;
|
|
296
|
+
my $parts = ProjectNameHelpers::gp($p, 'parts');
|
|
297
|
+
return Voxgig::Struct::islist($parts) ? scalar @$parts : 0;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
# The entity's own endpoint: a terminal `{param}` marks a record route, and
|
|
301
|
+
# among equals the shallower path wins (the same rule as make_point).
|
|
302
|
+
sub _pick_point {
|
|
303
|
+
my ($points) = @_;
|
|
304
|
+
return undef unless Voxgig::Struct::islist($points) && @$points;
|
|
305
|
+
my $point = $points->[0];
|
|
306
|
+
for my $cand (@$points[1 .. $#$points]) {
|
|
307
|
+
if (_point_terminal($cand) != _point_terminal($point)) {
|
|
308
|
+
$point = $cand if _point_terminal($cand);
|
|
309
|
+
}
|
|
310
|
+
elsif (_point_depth($cand) < _point_depth($point)) {
|
|
311
|
+
$point = $cand;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return $point;
|
|
315
|
+
}
|
|
316
|
+
|
|
293
317
|
sub build_args {
|
|
294
318
|
my ($self, $ctx, $op, $args) = @_;
|
|
295
319
|
my $opname = $op->{name};
|
|
296
320
|
my $entname = $ctx->{entity}->get_name;
|
|
297
321
|
my $points = ProjectNameHelpers::gpath($ctx->{config},
|
|
298
322
|
"entity.$entname.op.$opname.points");
|
|
299
|
-
my $point =
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
my $
|
|
323
|
+
my $point = _pick_point($points);
|
|
324
|
+
|
|
325
|
+
# Path AND query: a path-only read misses a query-addressed record
|
|
326
|
+
# (e.g. GET /result?trace_id=), which has no path param at all.
|
|
327
|
+
my $reqd = [];
|
|
328
|
+
for my $kind ('params', 'query') {
|
|
329
|
+
my $args_path = ProjectNameHelpers::gpath($point, "args.$kind");
|
|
330
|
+
my $reqd_args = Voxgig::Struct::select($args_path,
|
|
331
|
+
{ 'reqd' => Voxgig::Struct::JTRUE() });
|
|
332
|
+
my $names = Voxgig::Struct::transform($reqd_args, ['`$EACH`', '', '`$KEY.name`']);
|
|
333
|
+
push @$reqd, @$names if Voxgig::Struct::islist($names);
|
|
334
|
+
}
|
|
305
335
|
|
|
306
336
|
my $qand = [];
|
|
307
337
|
my $q = { '`$AND`' => $qand };
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
# Per-request timeout. Wraps the active transport with a deadline of "ms"
|
|
4
4
|
# milliseconds (default 30000; <= 0 disables). The transport is synchronous
|
|
5
5
|
# (HTTP::Tiny), so a hanging request is interrupted with a SIGALRM timer
|
|
6
|
-
# (Time::HiRes::ualarm)
|
|
7
|
-
#
|
|
8
|
-
#
|
|
6
|
+
# (Time::HiRes::ualarm) where the platform has one; where it has none
|
|
7
|
+
# (Windows) the deadline is checked once the call returns. When an
|
|
8
|
+
# injectable "now" clock is supplied the elapsed clock time is checked
|
|
9
|
+
# instead, so tests can assert the deadline deterministically. Expiry
|
|
10
|
+
# yields an error with code "timeout".
|
|
9
11
|
|
|
10
12
|
use strict;
|
|
11
13
|
use warnings;
|
|
@@ -75,24 +77,26 @@ sub with_timeout {
|
|
|
75
77
|
# Live path: interrupt a hanging synchronous transport.
|
|
76
78
|
my ($res, $err);
|
|
77
79
|
my $timed_out = 0;
|
|
78
|
-
my $
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
1;
|
|
85
|
-
};
|
|
80
|
+
my $start = Time::HiRes::time();
|
|
81
|
+
local $SIG{ALRM} = sub { $timed_out = 1; die "ProjectNameTimeout\n" };
|
|
82
|
+
my $armed = eval { Time::HiRes::ualarm(int($ms * 1000)); 1 };
|
|
83
|
+
my $ok = eval {
|
|
84
|
+
($res, $err) = $inner->($ctx, $url, $fetchdef);
|
|
85
|
+
1;
|
|
86
86
|
};
|
|
87
|
-
|
|
87
|
+
my $e = $@;
|
|
88
|
+
eval { Time::HiRes::ualarm(0) } if $armed;
|
|
88
89
|
if (!$ok) {
|
|
89
|
-
my $e = $@;
|
|
90
90
|
if ($timed_out) {
|
|
91
91
|
$self->_track($ctx, $ms);
|
|
92
92
|
return (undef, $ctx->make_error('timeout', "Request exceeded timeout of ${ms}ms"));
|
|
93
93
|
}
|
|
94
94
|
die $e;
|
|
95
95
|
}
|
|
96
|
+
if (!$armed && (Time::HiRes::time() - $start) * 1000 > $ms) {
|
|
97
|
+
$self->_track($ctx, $ms);
|
|
98
|
+
return (undef, $ctx->make_error('timeout', "Request exceeded timeout of ${ms}ms"));
|
|
99
|
+
}
|
|
96
100
|
return ($res, $err);
|
|
97
101
|
}
|
|
98
102
|
|
|
@@ -267,6 +267,15 @@ sub subset {
|
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
|
|
270
|
+
# Whether the SDK built the feature at all. The activity record is the
|
|
271
|
+
# wrong probe: most features create theirs on first use, so an idle client
|
|
272
|
+
# has none and every section but the eager ones read as inert.
|
|
273
|
+
sub present {
|
|
274
|
+
my ($client, $name) = @_;
|
|
275
|
+
return scalar grep { ref $_ && ($_->{name} // '') eq $name }
|
|
276
|
+
@{ $client->{features} || [] };
|
|
277
|
+
}
|
|
278
|
+
|
|
270
279
|
sub record {
|
|
271
280
|
my ($client, $name) = @_;
|
|
272
281
|
return $client->{"_$name"};
|
|
@@ -295,7 +304,11 @@ for my $name (sort keys %{ $CORPUS->{feature} || {} }) {
|
|
|
295
304
|
# Probed by ACTIVATING it: the feature defaults to inactive, so an idle
|
|
296
305
|
# client never builds it and its absence says nothing.
|
|
297
306
|
my $probe = build_client({ feature => [ { name => $name, active => 1 } ] });
|
|
298
|
-
|
|
307
|
+
unless (present($probe, $name)) {
|
|
308
|
+
# The one line every runner prints for an inert section.
|
|
309
|
+
diag("feature.$name: inert (this SDK does not generate the feature)");
|
|
310
|
+
next;
|
|
311
|
+
}
|
|
299
312
|
|
|
300
313
|
my %by_key = map { $_->{key} => $_ } @ops;
|
|
301
314
|
|
|
@@ -587,7 +587,13 @@ sub make_runner {
|
|
|
587
587
|
my ($testfile, $client) = @_;
|
|
588
588
|
|
|
589
589
|
my $specref = $testfile;
|
|
590
|
-
|
|
590
|
+
# A leading slash alone is not the whole of absolute: a Windows path
|
|
591
|
+
# starts `C:/` or `\\host\share`, and joining one onto $DIR reads
|
|
592
|
+
# nothing. Spelled out rather than left to File::Spec, which an MSYS perl
|
|
593
|
+
# resolves to the Unix flavour and which answers no for a drive letter.
|
|
594
|
+
if (defined $testfile && !ref $testfile
|
|
595
|
+
&& $testfile !~ m{^/} && $testfile !~ m{^[A-Za-z]:[\\/]}
|
|
596
|
+
&& $testfile !~ m{^\\\\}) {
|
|
591
597
|
$specref = Cwd::abs_path("$DIR/$testfile") || "$DIR/$testfile";
|
|
592
598
|
}
|
|
593
599
|
|
|
@@ -551,60 +551,157 @@ sub named_feature {
|
|
|
551
551
|
|
|
552
552
|
# === prepare_auth ===
|
|
553
553
|
|
|
554
|
+
# A cookie credential as prepare_auth writes it: `<scheme>=K` for the probe
|
|
555
|
+
# key, with no scheme prefix and nothing else in the bag.
|
|
556
|
+
sub _is_cookie_pair {
|
|
557
|
+
my ($value) = @_;
|
|
558
|
+
return (defined $value && !ref $value && $value =~ m{^[^=;]+=K$}) ? 1 : 0;
|
|
559
|
+
}
|
|
560
|
+
|
|
554
561
|
sub auth_ctx {
|
|
555
|
-
my ($options, $
|
|
562
|
+
my ($options, $spec) = @_;
|
|
556
563
|
my $ctx = ProjectNameContext->new({
|
|
557
564
|
'client' => PipelineOptClient->new($options),
|
|
558
565
|
'utility' => $utility,
|
|
559
566
|
'opname' => 'load',
|
|
560
567
|
}, undef);
|
|
561
|
-
$ctx->{spec} =
|
|
562
|
-
? ProjectNameSpec->new({ 'headers' => $headers, 'step' => 's' })
|
|
563
|
-
: undef;
|
|
568
|
+
$ctx->{spec} = $spec;
|
|
564
569
|
return $ctx;
|
|
565
570
|
}
|
|
566
571
|
|
|
572
|
+
sub auth_bags {
|
|
573
|
+
return ProjectNameSpec->new({ 'headers' => {}, 'query' => {}, 'step' => 's' });
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
# A cookie credential rides the header bag, because a cookie IS a header.
|
|
577
|
+
sub auth_bag {
|
|
578
|
+
my ($spec, $where) = @_;
|
|
579
|
+
return 'query' eq $where ? $spec->{query} : $spec->{headers};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
# `basic: false` is explicit: an HTTP Basic API's generated config carries
|
|
583
|
+
# `auth.basic: true`, and a client that merges it in takes a branch that needs
|
|
584
|
+
# a secret as well. With none supplied that branch deliberately writes
|
|
585
|
+
# nothing, which the probe would read as a public API.
|
|
586
|
+
sub auth_block {
|
|
587
|
+
my ($prefix) = @_;
|
|
588
|
+
return { 'prefix' => $prefix, 'basic' => 0 };
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
# Run prepare_auth with both containers present and see which one the
|
|
592
|
+
# generated utility writes to, and under what name. undef means this SDK
|
|
593
|
+
# places no credential at all - a public API - which is a legitimate shape,
|
|
594
|
+
# and the tests below assert exactly that instead. `pair` is the `<scheme>=`
|
|
595
|
+
# lead-in of a COOKIE credential, which rides the header bag under the key
|
|
596
|
+
# `cookie` instead of taking a header of its own.
|
|
597
|
+
sub auth_probe {
|
|
598
|
+
my ($options) = @_;
|
|
599
|
+
my $ctx = auth_ctx($options, auth_bags());
|
|
600
|
+
$utility->{prepare_auth}->($ctx);
|
|
601
|
+
for my $where ('headers', 'query') {
|
|
602
|
+
my $bag = auth_bag($ctx->{spec}, $where);
|
|
603
|
+
for my $name (keys %{ $bag || {} }) {
|
|
604
|
+
my $value = $bag->{$name};
|
|
605
|
+
my $pair = '';
|
|
606
|
+
if ('headers' eq $where && 'cookie' eq $name && _is_cookie_pair($value)) {
|
|
607
|
+
$pair = substr($value, 0, -1);
|
|
608
|
+
}
|
|
609
|
+
return { 'where' => $where, 'name' => $name,
|
|
610
|
+
'value' => $value, 'pair' => $pair };
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
return undef;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
sub auth_credential {
|
|
617
|
+
return auth_probe({ 'apikey' => 'K', 'auth' => auth_block('Bearer') });
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
# Every credential this SDK could possibly place: both credentials and Basic
|
|
621
|
+
# switched on, so whichever branch the API has, something lands unless the API
|
|
622
|
+
# is public.
|
|
623
|
+
sub auth_any_credential {
|
|
624
|
+
return auth_probe({ 'apikey' => 'K', 'secret' => 'S',
|
|
625
|
+
'auth' => { 'prefix' => 'Bearer', 'basic' => 1 } });
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
# Returns the value left in the credential slot, and whether it is there.
|
|
629
|
+
sub auth_placed {
|
|
630
|
+
my ($options, $seed) = @_;
|
|
631
|
+
my $cred = auth_credential();
|
|
632
|
+
my $spec = auth_bags();
|
|
633
|
+
if (defined $cred && defined $seed) {
|
|
634
|
+
auth_bag($spec, $cred->{where})->{ $cred->{name} } = $seed;
|
|
635
|
+
}
|
|
636
|
+
my $ctx = auth_ctx($options, $spec);
|
|
637
|
+
$utility->{prepare_auth}->($ctx);
|
|
638
|
+
return (undef, 0) if !defined $cred;
|
|
639
|
+
my $bag = auth_bag($ctx->{spec}, $cred->{where});
|
|
640
|
+
return ($bag->{ $cred->{name} }, exists $bag->{ $cred->{name} } ? 1 : 0);
|
|
641
|
+
}
|
|
642
|
+
|
|
567
643
|
{
|
|
568
|
-
my $ctx = auth_ctx({ 'auth' =>
|
|
644
|
+
my $ctx = auth_ctx({ 'auth' => auth_block(''), 'apikey' => 'K' }, undef);
|
|
569
645
|
my (undef, $err) = $utility->{prepare_auth}->($ctx);
|
|
570
646
|
is($err->{code}, 'auth_no_spec', 'prepare_auth guards a missing spec');
|
|
571
647
|
}
|
|
572
648
|
|
|
649
|
+
# Without this the cases below cannot fail for an SDK whose credential the
|
|
650
|
+
# probe misses: every one of them takes the public-API path instead.
|
|
573
651
|
{
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
ok(!defined $err, 'prepare_auth prefix: no error');
|
|
577
|
-
is($ctx->{spec}{headers}{authorization}, 'Bearer K',
|
|
578
|
-
'prepare_auth an apikey with a prefix is space-joined');
|
|
652
|
+
is(defined auth_credential() ? 1 : 0, defined auth_any_credential() ? 1 : 0,
|
|
653
|
+
'prepare_auth probe finds the credential this SDK places');
|
|
579
654
|
}
|
|
580
655
|
|
|
581
656
|
{
|
|
582
|
-
my $
|
|
583
|
-
|
|
584
|
-
|
|
657
|
+
my $cred = auth_credential();
|
|
658
|
+
if (!defined $cred) {
|
|
659
|
+
# A public API places nothing, and that is the whole assertion.
|
|
660
|
+
my (undef, $has) =
|
|
661
|
+
auth_placed({ 'apikey' => 'K', 'auth' => auth_block('Bearer') });
|
|
662
|
+
ok(!$has, 'prepare_auth places no credential for a public API');
|
|
663
|
+
}
|
|
664
|
+
elsif ('' ne $cred->{pair}) {
|
|
665
|
+
# A cookie credential is a `<scheme>=<key>` pair, and the scheme name
|
|
666
|
+
# leaves no room for the option's prefix.
|
|
667
|
+
ok(_is_cookie_pair($cred->{value}),
|
|
668
|
+
'prepare_auth places the apikey as a cookie pair');
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
671
|
+
ok('headers' eq $cred->{where} || 'query' eq $cred->{where},
|
|
672
|
+
'prepare_auth places the credential in headers or query');
|
|
673
|
+
# A header credential is prefix-joined; a query credential is the raw
|
|
674
|
+
# key, because a query parameter has nowhere to put a scheme name.
|
|
675
|
+
is($cred->{value}, 'query' eq $cred->{where} ? 'K' : 'Bearer K',
|
|
676
|
+
'prepare_auth places the apikey where this API puts it');
|
|
677
|
+
}
|
|
585
678
|
}
|
|
586
679
|
|
|
587
680
|
{
|
|
588
|
-
my $
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
681
|
+
my ($value, $has) = auth_placed({ 'apikey' => 'K', 'auth' => auth_block('') });
|
|
682
|
+
my $cred = auth_credential();
|
|
683
|
+
if (defined $cred) {
|
|
684
|
+
is($value, $cred->{pair} . 'K', 'prepare_auth a raw apikey goes in as-is');
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
ok(!$has, 'prepare_auth a raw apikey places nothing for a public API');
|
|
688
|
+
}
|
|
593
689
|
}
|
|
594
690
|
|
|
595
691
|
{
|
|
596
|
-
my
|
|
597
|
-
|
|
598
|
-
ok(
|
|
599
|
-
'prepare_auth a public api drops the header');
|
|
692
|
+
my (undef, $has) =
|
|
693
|
+
auth_placed({ 'apikey' => '', 'auth' => auth_block('Bearer') }, 'stale');
|
|
694
|
+
ok(!$has, 'prepare_auth an empty apikey drops the credential');
|
|
600
695
|
}
|
|
601
696
|
|
|
602
697
|
{
|
|
603
|
-
my $
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
698
|
+
my (undef, $has) = auth_placed({ 'apikey' => 'K' }, 'stale');
|
|
699
|
+
ok(!$has, 'prepare_auth a public api drops the credential');
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
{
|
|
703
|
+
my (undef, $has) = auth_placed({ 'auth' => auth_block('Bearer') }, 'stale');
|
|
704
|
+
ok(!$has, 'prepare_auth a missing apikey option drops the credential');
|
|
608
705
|
}
|
|
609
706
|
|
|
610
707
|
|
|
@@ -200,6 +200,64 @@ sub _def_setup {
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
|
|
203
|
+
# Where this SDK's prepare_auth actually puts the credential: the name is the
|
|
204
|
+
# API's OWN scheme name, not always `authorization`, so it is discovered by
|
|
205
|
+
# running prepare_auth once.
|
|
206
|
+
sub _auth_credential {
|
|
207
|
+
my ($auth_client) = @_;
|
|
208
|
+
my $auth_utility = $auth_client->get_utility;
|
|
209
|
+
my $ctx = ProjectNameContext->new({
|
|
210
|
+
'client' => $auth_client,
|
|
211
|
+
'utility' => $auth_utility,
|
|
212
|
+
'opname' => 'load',
|
|
213
|
+
}, undef);
|
|
214
|
+
# The DEF setup this client is built from carries `auth.basic: false`, so
|
|
215
|
+
# the single-token branch runs and there is a credential to find.
|
|
216
|
+
$ctx->{spec} = ProjectNameSpec->new({
|
|
217
|
+
'headers' => {}, 'query' => {}, 'step' => 's' });
|
|
218
|
+
my (undef, $err) = $auth_utility->{prepare_auth}->($ctx);
|
|
219
|
+
return undef if defined $err;
|
|
220
|
+
for my $where ('headers', 'query') {
|
|
221
|
+
# A cookie credential rides the header bag, because a cookie IS a header.
|
|
222
|
+
my $bag = 'query' eq $where ? $ctx->{spec}{query} : $ctx->{spec}{headers};
|
|
223
|
+
for my $name (keys %{ $bag || {} }) {
|
|
224
|
+
return { 'where' => $where, 'name' => $name };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return undef;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# Rename the corpus's `headers` bag to the real container, and the
|
|
232
|
+
# `authorization` key inside it to the real credential name. Applied only to
|
|
233
|
+
# the prepareAuth section, so real header assertions elsewhere are untouched.
|
|
234
|
+
sub _retarget_auth {
|
|
235
|
+
my ($node, $cred) = @_;
|
|
236
|
+
|
|
237
|
+
if (ref($node) eq 'ARRAY') {
|
|
238
|
+
return [ map { _retarget_auth($_, $cred) } @$node ];
|
|
239
|
+
}
|
|
240
|
+
return $node unless ref($node) eq 'HASH';
|
|
241
|
+
|
|
242
|
+
my $out = {};
|
|
243
|
+
for my $key (keys %$node) {
|
|
244
|
+
if ('headers' eq $key) {
|
|
245
|
+
my $bag = {};
|
|
246
|
+
my $inner = ref($node->{$key}) eq 'HASH' ? $node->{$key} : {};
|
|
247
|
+
for my $bagkey (keys %$inner) {
|
|
248
|
+
my $name = 'authorization' eq $bagkey ? $cred->{name} : $bagkey;
|
|
249
|
+
$bag->{$name} = _retarget_auth($inner->{$bagkey}, $cred);
|
|
250
|
+
}
|
|
251
|
+
$out->{ $cred->{where} } = $bag;
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
$out->{$key} = _retarget_auth($node->{$key}, $cred);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return $out;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
|
|
203
261
|
# === exists ===
|
|
204
262
|
|
|
205
263
|
for my $name (qw(
|
|
@@ -606,12 +664,27 @@ runsection('param', sub {
|
|
|
606
664
|
{
|
|
607
665
|
my $auth_client = ProjectNameSDK->test(undef, _def_setup('prepareAuth'));
|
|
608
666
|
|
|
667
|
+
# The corpus writes the credential as `headers.authorization`: a
|
|
668
|
+
# PLACEHOLDER each runner points at the container and name this API
|
|
669
|
+
# actually uses.
|
|
670
|
+
my $cred = _auth_credential($auth_client);
|
|
671
|
+
ok(defined $cred, 'prepare_auth placed a credential in headers or query');
|
|
672
|
+
|
|
673
|
+
# An absent section is runsection's report to make.
|
|
674
|
+
my $section = ref($spec) eq 'HASH' ? $spec->{prepareAuth} : undef;
|
|
675
|
+
my $swap = ref($section) eq 'HASH' && defined $cred
|
|
676
|
+
&& !('headers' eq $cred->{where} && 'authorization' eq $cred->{name});
|
|
677
|
+
my $original = $swap ? $section->{basic} : undef;
|
|
678
|
+
$section->{basic} = _retarget_auth($original, $cred) if $swap;
|
|
679
|
+
|
|
609
680
|
runsection('prepareAuth', sub {
|
|
610
681
|
my ($view) = @_;
|
|
611
682
|
my $ctx = ProjectNameOmni::livectx($view);
|
|
612
683
|
$ctx->{client} = $auth_client;
|
|
613
684
|
return _unwrap($utility->{prepare_auth}->($ctx));
|
|
614
685
|
});
|
|
686
|
+
|
|
687
|
+
$section->{basic} = $original if $swap;
|
|
615
688
|
}
|
|
616
689
|
|
|
617
690
|
|
|
@@ -16,6 +16,16 @@ package ProjectNameUtilities;
|
|
|
16
16
|
|
|
17
17
|
our %REGISTRY;
|
|
18
18
|
|
|
19
|
+
# One HTTP::Tiny per (proxy, redirect) setting: a client keeps its
|
|
20
|
+
# connection alive between requests, so one per request reused nothing.
|
|
21
|
+
my %HTTP_CLIENTS;
|
|
22
|
+
|
|
23
|
+
sub _http_client {
|
|
24
|
+
my (%new_args) = @_;
|
|
25
|
+
my $key = join "\n", map { "$_=$new_args{$_}" } sort keys %new_args;
|
|
26
|
+
return $HTTP_CLIENTS{$key} //= HTTP::Tiny->new(%new_args);
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
our $DefaultHttpFetch = sub {
|
|
20
30
|
my ($fullurl, $fetchdef) = @_;
|
|
21
31
|
$fetchdef = {} unless defined $fetchdef;
|
|
@@ -47,8 +57,12 @@ our $DefaultHttpFetch = sub {
|
|
|
47
57
|
$new_args{max_redirect} = 0
|
|
48
58
|
if defined $fetchdef->{redirect} && !ref $fetchdef->{redirect}
|
|
49
59
|
&& 'manual' eq $fetchdef->{redirect};
|
|
60
|
+
# Both keys: HTTP::Tiny lets the environment override its generic proxy.
|
|
61
|
+
$new_args{http_proxy} = $new_args{https_proxy} = "$fetchdef->{proxy}"
|
|
62
|
+
if defined $fetchdef->{proxy} && !ref $fetchdef->{proxy}
|
|
63
|
+
&& length $fetchdef->{proxy};
|
|
50
64
|
|
|
51
|
-
my $res = eval {
|
|
65
|
+
my $res = eval { _http_client(%new_args)->request($method, $fullurl, $opts) };
|
|
52
66
|
if (!$res) {
|
|
53
67
|
my $e = defined $@ ? "$@" : 'request failed';
|
|
54
68
|
$e =~ s/\s+\z//;
|
|
@@ -30,6 +30,7 @@ $REGISTRY{prepare_query} = sub {
|
|
|
30
30
|
for my $item (@$items) {
|
|
31
31
|
my ($key, $val) = @$item;
|
|
32
32
|
next unless ProjectNameHelpers::rb_truthy($val) && defined $key && !ref $key;
|
|
33
|
+
next if '$action' eq $key;
|
|
33
34
|
next if grep { defined $_ && !ref $_ && $_ eq $key } @$params;
|
|
34
35
|
$out->{$key} = $val;
|
|
35
36
|
}
|