@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
|
@@ -15,16 +15,26 @@ package ProjectNameUtilities;
|
|
|
15
15
|
|
|
16
16
|
our %REGISTRY;
|
|
17
17
|
|
|
18
|
+
# `$action` selects the point (see make_point); it is never an API field, so
|
|
19
|
+
# the body is a copy without it. The caller's hash is left untouched.
|
|
20
|
+
my $strip_action = sub {
|
|
21
|
+
my ($reqdata) = @_;
|
|
22
|
+
return $reqdata unless 'HASH' eq ref $reqdata && exists $reqdata->{'$action'};
|
|
23
|
+
my %body = %$reqdata;
|
|
24
|
+
delete $body{'$action'};
|
|
25
|
+
return \%body;
|
|
26
|
+
};
|
|
27
|
+
|
|
18
28
|
$REGISTRY{transform_request} = sub {
|
|
19
29
|
my ($ctx) = @_;
|
|
20
30
|
my $spec = $ctx->{spec};
|
|
21
31
|
my $point = $ctx->{point};
|
|
22
32
|
$spec->{step} = 'reqform' if $spec;
|
|
23
33
|
my $transform = ProjectNameHelpers::to_map(ProjectNameHelpers::gp($point, 'transform'));
|
|
24
|
-
return $ctx->{reqdata} unless $transform;
|
|
34
|
+
return $strip_action->($ctx->{reqdata}) unless $transform;
|
|
25
35
|
my $reqform = ProjectNameHelpers::gp($transform, 'req');
|
|
26
|
-
return $ctx->{reqdata} unless ProjectNameHelpers::rb_truthy($reqform);
|
|
27
|
-
return Voxgig::Struct::transform({ 'reqdata' => $ctx->{reqdata} }, $reqform);
|
|
36
|
+
return $strip_action->($ctx->{reqdata}) unless ProjectNameHelpers::rb_truthy($reqform);
|
|
37
|
+
return $strip_action->(Voxgig::Struct::transform({ 'reqdata' => $ctx->{reqdata} }, $reqform));
|
|
28
38
|
};
|
|
29
39
|
|
|
30
40
|
1;
|
|
@@ -55,7 +55,11 @@ class ProjectNameContext
|
|
|
55
55
|
if (array_key_exists('actor', $ctrl_raw)) {
|
|
56
56
|
$this->ctrl->actor = $ctrl_raw['actor'];
|
|
57
57
|
}
|
|
58
|
-
|
|
58
|
+
if (isset($ctrl_raw['paging']) && is_array($ctrl_raw['paging'])) {
|
|
59
|
+
$this->ctrl->paging = $ctrl_raw['paging'];
|
|
60
|
+
}
|
|
61
|
+
} elseif ($basectx !== null && $basectx->ctrl !== null
|
|
62
|
+
&& ProjectNameHelpers::get_ctx_prop($ctxmap, "opname") === null) {
|
|
59
63
|
$this->ctrl = $basectx->ctrl;
|
|
60
64
|
}
|
|
61
65
|
|
|
@@ -21,11 +21,16 @@ class ProjectNameControl
|
|
|
21
21
|
// passed. The shared feature corpus caught it.
|
|
22
22
|
public mixed $actor;
|
|
23
23
|
|
|
24
|
+
// Per-call paging record: read by the paging feature before the request
|
|
25
|
+
// and written back after it. Declared for the same reason as `$actor`.
|
|
26
|
+
public mixed $paging;
|
|
27
|
+
|
|
24
28
|
public function __construct(array $opts = [])
|
|
25
29
|
{
|
|
26
30
|
$this->throw_err = $opts['throw_err'] ?? null;
|
|
27
31
|
$this->err = null;
|
|
28
32
|
$this->explain = $opts['explain'] ?? null;
|
|
29
33
|
$this->actor = $opts['actor'] ?? null;
|
|
34
|
+
$this->paging = $opts['paging'] ?? null;
|
|
30
35
|
}
|
|
31
36
|
}
|
|
@@ -9,8 +9,8 @@ require_once __DIR__ . '/BaseFeature.php';
|
|
|
9
9
|
// stamps page/limit (or a cursor) into the request query; on the way back
|
|
10
10
|
// (PreResult) it reads the server's pagination signals — a `Link:
|
|
11
11
|
// rel="next"` header, `X-Page`/`X-Next-Page`/`X-Total-Count` headers, or
|
|
12
|
-
// `next`/`cursor`/`nextCursor`/`hasMore` fields in the body
|
|
13
|
-
// them on `$ctx->result->paging`. A per-call `ctrl->paging` (page or
|
|
12
|
+
// `next`/`cursor`/`nextCursor`/`hasMore` fields in the body (snake_case
|
|
13
|
+
// forms too) — and records them on `$ctx->result->paging` and `ctrl->paging`. A per-call `ctrl->paging` (page or
|
|
14
14
|
// cursor) takes priority over the stamped defaults; parameter names and
|
|
15
15
|
// page size are configurable; `startPage` defaults to 1. Mirrors
|
|
16
16
|
// ts/src/feature/paging/PagingFeature.ts.
|
|
@@ -71,8 +71,10 @@ class ProjectNamePagingFeature extends ProjectNameBaseFeature
|
|
|
71
71
|
} elseif (($spec->query[$page_param] ?? null) === null) {
|
|
72
72
|
$start_page = is_numeric($this->options['startPage'] ?? null)
|
|
73
73
|
? $this->options['startPage'] : 1;
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
// A record written back by PreResult holds the page just fetched
|
|
75
|
+
// as "page" and the one to fetch as "nextPage", so nextPage wins.
|
|
76
|
+
$page = $paging['nextPage'] ?? $paging['page'] ?? null;
|
|
77
|
+
$spec->query[$page_param] = $page !== null ? $page : $start_page;
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
if (($this->options['limit'] ?? null) !== null
|
|
@@ -194,17 +196,30 @@ class ProjectNamePagingFeature extends ProjectNameBaseFeature
|
|
|
194
196
|
}
|
|
195
197
|
}
|
|
196
198
|
|
|
197
|
-
// Body-level
|
|
199
|
+
// Body-level signals; snake_case forms are read first so camelCase wins.
|
|
198
200
|
if (is_array($body)) {
|
|
199
201
|
if (($body['next'] ?? null) !== null) {
|
|
200
202
|
$paging['next'] = $paging['next'] ?? $body['next'];
|
|
201
203
|
}
|
|
204
|
+
if (($body['next_cursor'] ?? null) !== null) {
|
|
205
|
+
$paging['cursor'] = $body['next_cursor'];
|
|
206
|
+
}
|
|
202
207
|
if (($body['cursor'] ?? null) !== null) {
|
|
203
208
|
$paging['cursor'] = $body['cursor'];
|
|
204
209
|
}
|
|
205
210
|
if (($body['nextCursor'] ?? null) !== null) {
|
|
206
211
|
$paging['cursor'] = $body['nextCursor'];
|
|
207
212
|
}
|
|
213
|
+
if ($paging['nextPage'] === null) {
|
|
214
|
+
$np = $body['nextPage'] ?? $body['next_page'] ?? null;
|
|
215
|
+
if (is_int($np) || is_float($np) || is_string($np)) {
|
|
216
|
+
$paging['nextPage'] = $np;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (is_bool($body['has_more'] ?? null)) {
|
|
220
|
+
$paging['hasMore'] = $body['has_more'];
|
|
221
|
+
$explicit_more = true;
|
|
222
|
+
}
|
|
208
223
|
if (is_bool($body['hasMore'] ?? null)) {
|
|
209
224
|
$paging['hasMore'] = $body['hasMore'];
|
|
210
225
|
$explicit_more = true;
|
|
@@ -225,6 +240,12 @@ class ProjectNamePagingFeature extends ProjectNameBaseFeature
|
|
|
225
240
|
|
|
226
241
|
$result->paging = $paging;
|
|
227
242
|
|
|
243
|
+
// PHP arrays are values, so the record lands on this call's Control
|
|
244
|
+
// rather than on an array the caller passed.
|
|
245
|
+
if ($ctx->ctrl !== null) {
|
|
246
|
+
$ctx->ctrl->paging = $paging;
|
|
247
|
+
}
|
|
248
|
+
|
|
228
249
|
$this->client->_paging = ['last' => $paging];
|
|
229
250
|
}
|
|
230
251
|
|
|
@@ -35,8 +35,8 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
35
35
|
|
|
36
36
|
$this->client->mode = 'test';
|
|
37
37
|
|
|
38
|
-
// Ensure entity ids are correct.
|
|
39
|
-
\Voxgig\Struct\Struct::walk($entity_data, function ($key, $val, $parent, $path) {
|
|
38
|
+
// Ensure entity ids are correct (walk is by value: keep its result).
|
|
39
|
+
$entity_data = \Voxgig\Struct\Struct::walk($entity_data, function ($key, $val, $parent, $path) {
|
|
40
40
|
if (count($path) === 2 && is_array($val) && $key !== null) {
|
|
41
41
|
$val['id'] = $key;
|
|
42
42
|
}
|
|
@@ -103,9 +103,8 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
103
103
|
|
|
104
104
|
// PHP-portable equivalent of TS buildArgs+select: a flat-key
|
|
105
105
|
// filter that matches by exact-equality on each provided key,
|
|
106
|
-
// with alias fallback.
|
|
107
|
-
//
|
|
108
|
-
// create), list returns all entries.
|
|
106
|
+
// with alias fallback. An empty match matches every entry, as an
|
|
107
|
+
// empty `$AND` does in select; a non-empty miss matches nothing.
|
|
109
108
|
$find_first = function (array $entmap, $match, $alias) {
|
|
110
109
|
if (!is_array($match) || empty($match)) {
|
|
111
110
|
foreach ($entmap as $e) {
|
|
@@ -158,6 +157,23 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
158
157
|
|
|
159
158
|
$alias = is_object($op) ? ($op->alias ?? null) : \Voxgig\Struct\Struct::getprop($op, 'alias');
|
|
160
159
|
|
|
160
|
+
// Every op filters through buildArgs, which keeps `id` and the
|
|
161
|
+
// point's required params and drops the rest. find_first/find_all
|
|
162
|
+
// take a flat map, so take the primary key of each $OR clause;
|
|
163
|
+
// they do their own alias fallback.
|
|
164
|
+
$query_match = function ($args) use ($fctx, $op) {
|
|
165
|
+
$flat = [];
|
|
166
|
+
foreach (($this->buildArgs($fctx, $op, $args)['$AND'] ?? []) as $clause) {
|
|
167
|
+
$ors = $clause['$OR'] ?? [];
|
|
168
|
+
if (is_array($ors) && count($ors) > 0 && is_array($ors[0])) {
|
|
169
|
+
foreach ($ors[0] as $lk => $lv) {
|
|
170
|
+
$flat[$lk] = $lv;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return $flat;
|
|
175
|
+
};
|
|
176
|
+
|
|
161
177
|
// For single-entity ops (load, remove) with an empty explicit
|
|
162
178
|
// match, fall back to the id the entity client already knows from a
|
|
163
179
|
// prior create/load (carried in $fctx->match / $fctx->data). This
|
|
@@ -176,7 +192,7 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
176
192
|
};
|
|
177
193
|
|
|
178
194
|
if ($op->name === 'load') {
|
|
179
|
-
$ent = $find_first($entmap, $resolve_match($fctx->reqmatch), $alias);
|
|
195
|
+
$ent = $find_first($entmap, $query_match($resolve_match($fctx->reqmatch)), $alias);
|
|
180
196
|
if ($ent === null) {
|
|
181
197
|
return $respond(404, null, ['statusText' => 'Not found']);
|
|
182
198
|
}
|
|
@@ -185,37 +201,7 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
185
201
|
return $respond(200, $out);
|
|
186
202
|
|
|
187
203
|
} elseif ($op->name === 'list') {
|
|
188
|
-
|
|
189
|
-
//
|
|
190
|
-
// This branch used the whole of reqmatch, while load goes
|
|
191
|
-
// through resolve_match and the TS mock runs every op through
|
|
192
|
-
// buildArgs — which keeps `id` and the point's REQUIRED params
|
|
193
|
-
// and drops the rest. buildArgs is right here in this file and
|
|
194
|
-
// its docblock says it mirrors the TS one; list simply did not
|
|
195
|
-
// call it.
|
|
196
|
-
//
|
|
197
|
-
// So a match key that is neither `id` nor a required route
|
|
198
|
-
// param matched NOTHING in php and was IGNORED in ts. trello's
|
|
199
|
-
// board_star lists on `board_id`, which is not a field of that
|
|
200
|
-
// entity (id, pos, member_id, id_board) and not a route param:
|
|
201
|
-
// php returned an empty list and BoardStarEntityTest failed on
|
|
202
|
-
// an assertion that ts, js and every other port passed.
|
|
203
|
-
//
|
|
204
|
-
// buildArgs returns a struct query; find_all takes a flat map,
|
|
205
|
-
// so take the primary key of each $OR clause. find_all already
|
|
206
|
-
// does its own alias fallback, which is what the second $OR
|
|
207
|
-
// element carries.
|
|
208
|
-
$listargs = $this->buildArgs($fctx, $op, $fctx->reqmatch);
|
|
209
|
-
$listmatch = [];
|
|
210
|
-
foreach (($listargs['$AND'] ?? []) as $clause) {
|
|
211
|
-
$ors = $clause['$OR'] ?? [];
|
|
212
|
-
if (is_array($ors) && count($ors) > 0 && is_array($ors[0])) {
|
|
213
|
-
foreach ($ors[0] as $lk => $lv) {
|
|
214
|
-
$listmatch[$lk] = $lv;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
$found = $find_all($entmap, $listmatch, $alias);
|
|
204
|
+
$found = $find_all($entmap, $query_match($fctx->reqmatch), $alias);
|
|
219
205
|
$cleaned = [];
|
|
220
206
|
foreach ($found as $e) {
|
|
221
207
|
if (is_array($e)) unset($e['$KEY']);
|
|
@@ -245,14 +231,13 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
245
231
|
if (empty($update_match)) {
|
|
246
232
|
$update_match = $resolve_match([]);
|
|
247
233
|
}
|
|
248
|
-
$ent = $find_first($entmap, $update_match, $alias);
|
|
234
|
+
$ent = $find_first($entmap, $query_match($update_match), $alias);
|
|
249
235
|
if ($ent === null) {
|
|
236
|
+
// update miss: 404, never another record
|
|
250
237
|
return $respond(404, null, ['statusText' => 'Not found']);
|
|
251
238
|
}
|
|
252
239
|
if (is_array($fctx->reqdata)) {
|
|
253
|
-
|
|
254
|
-
$ent[$k] = $v;
|
|
255
|
-
}
|
|
240
|
+
$ent = \Voxgig\Struct\Struct::merge([$ent, $fctx->reqdata]);
|
|
256
241
|
}
|
|
257
242
|
$id = is_array($ent) ? ($ent['id'] ?? null) : null;
|
|
258
243
|
if ($id !== null) {
|
|
@@ -264,7 +249,7 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
264
249
|
return $respond(200, $out);
|
|
265
250
|
|
|
266
251
|
} elseif ($op->name === 'remove') {
|
|
267
|
-
$ent = $find_first($entmap, $resolve_match($fctx->reqmatch), $alias);
|
|
252
|
+
$ent = $find_first($entmap, $query_match($resolve_match($fctx->reqmatch)), $alias);
|
|
268
253
|
// Remove only the first matched entity. If nothing matches,
|
|
269
254
|
// succeed as a no-op rather than erroring.
|
|
270
255
|
$id = is_array($ent) ? ($ent['id'] ?? null) : null;
|
|
@@ -267,6 +267,19 @@ class FeatureCorpusTest extends TestCase
|
|
|
267
267
|
$this->assertSame($expect, $actual, $path);
|
|
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
|
|
272
|
+
// client has none and every section but the eager ones read as inert.
|
|
273
|
+
private static function present($client, string $name): bool
|
|
274
|
+
{
|
|
275
|
+
foreach ($client->features ?? [] as $f) {
|
|
276
|
+
if (($f->name ?? null) === $name) {
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
|
|
270
283
|
private static function record($client, string $name)
|
|
271
284
|
{
|
|
272
285
|
$prop = '_' . $name;
|
|
@@ -326,7 +339,10 @@ class FeatureCorpusTest extends TestCase
|
|
|
326
339
|
// Probed by ACTIVATING it: the feature defaults to inactive, so an
|
|
327
340
|
// idle client never builds it and its absence says nothing.
|
|
328
341
|
$probe = self::buildClient(['feature' => [['name' => $name, 'active' => true]]]);
|
|
329
|
-
if (
|
|
342
|
+
if (!self::present($probe, $name)) {
|
|
343
|
+
// The one line every runner prints for an inert section.
|
|
344
|
+
fwrite(STDERR, sprintf(
|
|
345
|
+
"feature.%s: inert (this SDK does not generate the feature)\n", $name));
|
|
330
346
|
continue;
|
|
331
347
|
}
|
|
332
348
|
|
|
@@ -1104,6 +1104,50 @@ class FeatureTest extends TestCase
|
|
|
1104
1104
|
$this->assertTrue($res['result']->paging['hasMore']);
|
|
1105
1105
|
}
|
|
1106
1106
|
|
|
1107
|
+
public function test_paging_snake_case_signals_and_ctrl_write_back(): void
|
|
1108
|
+
{
|
|
1109
|
+
$this->needs('paging');
|
|
1110
|
+
$rec = new FtRecorder(function (int $n) {
|
|
1111
|
+
return FtHarness::response(200, 1 === $n
|
|
1112
|
+
? ['has_more' => true, 'next_cursor' => 'c2']
|
|
1113
|
+
: ['has_more' => false]);
|
|
1114
|
+
});
|
|
1115
|
+
$h = new FtHarness([
|
|
1116
|
+
['name' => 'paging'],
|
|
1117
|
+
], $rec->server());
|
|
1118
|
+
$res = $h->op(['op' => 'list', 'path' => '/w', 'ctrl' => ['paging' => []]]);
|
|
1119
|
+
$this->assertTrue($res['result']->paging['hasMore']);
|
|
1120
|
+
$this->assertSame('c2', $res['result']->paging['cursor']);
|
|
1121
|
+
$written = $res['ctx']->ctrl->paging;
|
|
1122
|
+
$this->assertSame('c2', $written['cursor']);
|
|
1123
|
+
$this->assertTrue($written['hasMore']);
|
|
1124
|
+
// Arrays are values here, so the written-back record is passed on by hand.
|
|
1125
|
+
$res = $h->op(['op' => 'list', 'path' => '/w', 'ctrl' => ['paging' => $written]]);
|
|
1126
|
+
$this->assertStringContainsString('cursor=c2', $rec->calls[1]['url']);
|
|
1127
|
+
$this->assertFalse($res['ctx']->ctrl->paging['hasMore']);
|
|
1128
|
+
$this->assertNull($res['ctx']->ctrl->paging['cursor']);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
public function test_paging_continues_from_written_back_next_page(): void
|
|
1132
|
+
{
|
|
1133
|
+
$this->needs('paging');
|
|
1134
|
+
$rec = new FtRecorder(function (int $n) {
|
|
1135
|
+
return FtHarness::response(200, 1 === $n ? ['next_page' => 2] : [],
|
|
1136
|
+
['x-page' => (string) $n]);
|
|
1137
|
+
});
|
|
1138
|
+
$h = new FtHarness([
|
|
1139
|
+
['name' => 'paging'],
|
|
1140
|
+
], $rec->server());
|
|
1141
|
+
$res = $h->op(['op' => 'list', 'path' => '/w']);
|
|
1142
|
+
$written = $res['ctx']->ctrl->paging;
|
|
1143
|
+
$this->assertEquals(1, $written['page']);
|
|
1144
|
+
$this->assertEquals(2, $written['nextPage']);
|
|
1145
|
+
$this->assertTrue($written['hasMore']);
|
|
1146
|
+
$res = $h->op(['op' => 'list', 'path' => '/w', 'ctrl' => ['paging' => $written]]);
|
|
1147
|
+
$this->assertStringContainsString('page=2', $rec->calls[1]['url']);
|
|
1148
|
+
$this->assertFalse($res['ctx']->ctrl->paging['hasMore']);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1107
1151
|
public function test_paging_non_list_op_is_not_paged(): void
|
|
1108
1152
|
{
|
|
1109
1153
|
$this->needs('paging');
|
|
@@ -611,59 +611,176 @@ class PipelineTest extends TestCase
|
|
|
611
611
|
|
|
612
612
|
// --- prepare_auth ------------------------------------------------------------
|
|
613
613
|
|
|
614
|
-
|
|
614
|
+
/**
|
|
615
|
+
* A cookie credential as prepare_auth writes it: `<scheme>=K` for the
|
|
616
|
+
* probe key, with no scheme prefix and nothing else in the bag.
|
|
617
|
+
*/
|
|
618
|
+
private const COOKIE_PAIR = '/^[^=;]+=K$/';
|
|
619
|
+
|
|
620
|
+
private static function auth_ctx(array $options, ?ProjectNameSpec $spec): ProjectNameContext
|
|
615
621
|
{
|
|
616
622
|
$client = new PlClient($options);
|
|
617
623
|
return self::ctx([
|
|
618
624
|
'client' => $client,
|
|
619
|
-
'spec' => $
|
|
625
|
+
'spec' => $spec,
|
|
626
|
+
]);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
private static function auth_bags(): ProjectNameSpec
|
|
630
|
+
{
|
|
631
|
+
return new ProjectNameSpec(['headers' => [], 'query' => []]);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* `basic: false` is explicit: an HTTP Basic API's generated config carries
|
|
636
|
+
* `auth.basic: true`, and a client that merges it in takes a branch that
|
|
637
|
+
* needs a secret as well. With none supplied that branch deliberately
|
|
638
|
+
* writes nothing, which the probe would read as a public API.
|
|
639
|
+
*/
|
|
640
|
+
private static function auth_block(string $prefix): array
|
|
641
|
+
{
|
|
642
|
+
return ['prefix' => $prefix, 'basic' => false];
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Run prepare_auth with both containers present and see which one the
|
|
647
|
+
* generated utility writes to, and under what name. Null means this SDK
|
|
648
|
+
* places no credential at all - a public API - which is a legitimate
|
|
649
|
+
* shape, and the tests below assert exactly that instead. `pair` is the
|
|
650
|
+
* `<scheme>=` lead-in of a COOKIE credential, which rides the header bag
|
|
651
|
+
* under the key `cookie` instead of taking a header of its own.
|
|
652
|
+
*
|
|
653
|
+
* @return array{where:string,name:string,value:mixed,pair:string}|null
|
|
654
|
+
*/
|
|
655
|
+
private static function auth_probe(array $options): ?array
|
|
656
|
+
{
|
|
657
|
+
$ctx = self::auth_ctx($options, self::auth_bags());
|
|
658
|
+
ProjectNamePrepareAuth::call($ctx);
|
|
659
|
+
foreach (['headers', 'query'] as $where) {
|
|
660
|
+
// A cookie credential rides the header bag, because a cookie IS
|
|
661
|
+
// a header.
|
|
662
|
+
$bag = 'query' === $where ? $ctx->spec->query : $ctx->spec->headers;
|
|
663
|
+
foreach ($bag as $name => $value) {
|
|
664
|
+
$pair = '';
|
|
665
|
+
if ('headers' === $where && 'cookie' === $name && is_string($value)
|
|
666
|
+
&& 1 === preg_match(self::COOKIE_PAIR, $value)) {
|
|
667
|
+
$pair = substr($value, 0, -1);
|
|
668
|
+
}
|
|
669
|
+
return ['where' => $where, 'name' => $name, 'value' => $value, 'pair' => $pair];
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/** @return array{where:string,name:string,value:mixed,pair:string}|null */
|
|
676
|
+
private static function auth_credential(): ?array
|
|
677
|
+
{
|
|
678
|
+
return self::auth_probe(
|
|
679
|
+
['apikey' => 'K', 'auth' => self::auth_block('Bearer')]);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Every credential this SDK could possibly place: both credentials and
|
|
684
|
+
* Basic switched on, so whichever branch the API has, something lands
|
|
685
|
+
* unless the API is public.
|
|
686
|
+
*
|
|
687
|
+
* @return array{where:string,name:string,value:mixed,pair:string}|null
|
|
688
|
+
*/
|
|
689
|
+
private static function auth_any_credential(): ?array
|
|
690
|
+
{
|
|
691
|
+
return self::auth_probe([
|
|
692
|
+
'apikey' => 'K', 'secret' => 'S',
|
|
693
|
+
'auth' => ['prefix' => 'Bearer', 'basic' => true],
|
|
620
694
|
]);
|
|
621
695
|
}
|
|
622
696
|
|
|
697
|
+
/** @return array{0:mixed,1:bool} the value left in the credential slot, and whether it is there */
|
|
698
|
+
private static function auth_placed(array $options, $seed = null): array
|
|
699
|
+
{
|
|
700
|
+
$cred = self::auth_credential();
|
|
701
|
+
$spec = self::auth_bags();
|
|
702
|
+
if (null !== $cred && null !== $seed) {
|
|
703
|
+
if ('query' === $cred['where']) {
|
|
704
|
+
$spec->query[$cred['name']] = $seed;
|
|
705
|
+
} else {
|
|
706
|
+
$spec->headers[$cred['name']] = $seed;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
$ctx = self::auth_ctx($options, $spec);
|
|
710
|
+
ProjectNamePrepareAuth::call($ctx);
|
|
711
|
+
if (null === $cred) {
|
|
712
|
+
return [null, false];
|
|
713
|
+
}
|
|
714
|
+
$bag = 'query' === $cred['where'] ? $ctx->spec->query : $ctx->spec->headers;
|
|
715
|
+
return [$bag[$cred['name']] ?? null, array_key_exists($cred['name'], $bag)];
|
|
716
|
+
}
|
|
717
|
+
|
|
623
718
|
public function test_prepare_auth_guards_a_missing_spec(): void
|
|
624
719
|
{
|
|
625
|
-
$ctx = self::auth_ctx(['auth' =>
|
|
720
|
+
$ctx = self::auth_ctx(['auth' => self::auth_block(''), 'apikey' => 'K'], null);
|
|
626
721
|
[, $err] = ProjectNamePrepareAuth::call($ctx);
|
|
627
722
|
$this->assertSame('auth_no_spec', self::code($err));
|
|
628
723
|
}
|
|
629
724
|
|
|
630
|
-
|
|
725
|
+
/**
|
|
726
|
+
* Without this the cases below cannot fail for an SDK whose credential the
|
|
727
|
+
* probe misses: every one of them takes the public-API path instead.
|
|
728
|
+
*/
|
|
729
|
+
public function test_prepare_auth_probe_finds_the_credential_this_sdk_places(): void
|
|
631
730
|
{
|
|
632
|
-
$
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
731
|
+
$this->assertSame(
|
|
732
|
+
null === self::auth_credential(), null === self::auth_any_credential());
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
public function test_prepare_auth_places_the_apikey_where_this_api_puts_it(): void
|
|
736
|
+
{
|
|
737
|
+
$cred = self::auth_credential();
|
|
738
|
+
if (null === $cred) {
|
|
739
|
+
// A public API places nothing, and that is the whole assertion.
|
|
740
|
+
[, $has] = self::auth_placed(['apikey' => 'K', 'auth' => self::auth_block('Bearer')]);
|
|
741
|
+
$this->assertFalse($has);
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
$this->assertContains($cred['where'], ['headers', 'query']);
|
|
745
|
+
if ('' !== $cred['pair']) {
|
|
746
|
+
// A cookie credential is a `<scheme>=<key>` pair, and the scheme
|
|
747
|
+
// name leaves no room for the option's prefix.
|
|
748
|
+
$this->assertMatchesRegularExpression(self::COOKIE_PAIR, $cred['value']);
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
// A header credential is prefix-joined; a query credential is the raw
|
|
752
|
+
// key, because a query parameter has nowhere to put a scheme name.
|
|
753
|
+
$this->assertSame('query' === $cred['where'] ? 'K' : 'Bearer K', $cred['value']);
|
|
636
754
|
}
|
|
637
755
|
|
|
638
756
|
public function test_prepare_auth_a_raw_apikey_goes_in_as_is(): void
|
|
639
757
|
{
|
|
640
|
-
$
|
|
641
|
-
|
|
642
|
-
|
|
758
|
+
[$value, $has] = self::auth_placed(['apikey' => 'K', 'auth' => self::auth_block('')]);
|
|
759
|
+
$cred = self::auth_credential();
|
|
760
|
+
if (null === $cred) {
|
|
761
|
+
$this->assertFalse($has);
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
$this->assertSame($cred['pair'] . 'K', $value);
|
|
643
765
|
}
|
|
644
766
|
|
|
645
|
-
public function
|
|
767
|
+
public function test_prepare_auth_an_empty_apikey_drops_the_credential(): void
|
|
646
768
|
{
|
|
647
|
-
$
|
|
648
|
-
['apikey' => '', 'auth' =>
|
|
649
|
-
|
|
650
|
-
);
|
|
651
|
-
ProjectNamePrepareAuth::call($ctx);
|
|
652
|
-
$this->assertArrayNotHasKey('authorization', $ctx->spec->headers);
|
|
769
|
+
[, $has] = self::auth_placed(
|
|
770
|
+
['apikey' => '', 'auth' => self::auth_block('Bearer')], 'stale');
|
|
771
|
+
$this->assertFalse($has);
|
|
653
772
|
}
|
|
654
773
|
|
|
655
|
-
public function
|
|
774
|
+
public function test_prepare_auth_a_public_api_drops_the_credential(): void
|
|
656
775
|
{
|
|
657
|
-
$
|
|
658
|
-
|
|
659
|
-
$this->assertArrayNotHasKey('authorization', $ctx->spec->headers);
|
|
776
|
+
[, $has] = self::auth_placed(['apikey' => 'K'], 'stale');
|
|
777
|
+
$this->assertFalse($has);
|
|
660
778
|
}
|
|
661
779
|
|
|
662
|
-
public function
|
|
780
|
+
public function test_prepare_auth_a_missing_apikey_option_drops_the_credential(): void
|
|
663
781
|
{
|
|
664
|
-
$
|
|
665
|
-
|
|
666
|
-
$this->assertArrayNotHasKey('authorization', $ctx->spec->headers);
|
|
782
|
+
[, $has] = self::auth_placed(['auth' => self::auth_block('Bearer')], 'stale');
|
|
783
|
+
$this->assertFalse($has);
|
|
667
784
|
}
|
|
668
785
|
|
|
669
786
|
|
|
@@ -70,7 +70,8 @@ class PrimaryUtilityTest extends TestCase
|
|
|
70
70
|
* `set` used to pass silently, which defeats the point of a shared
|
|
71
71
|
* oracle. EVERY corpus-backed test goes through here (mirrors ts/py).
|
|
72
72
|
*/
|
|
73
|
-
private function runsection(
|
|
73
|
+
private function runsection(
|
|
74
|
+
string $name, callable $subject, ?callable $adapt = null): void
|
|
74
75
|
{
|
|
75
76
|
$R = self::runpack();
|
|
76
77
|
$spec = $R['spec'];
|
|
@@ -89,6 +90,10 @@ class PrimaryUtilityTest extends TestCase
|
|
|
89
90
|
"add cases, or mark the fixture PENDING in .sdk/test/primary/");
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
if (null !== $adapt) {
|
|
94
|
+
$basic = $adapt($basic);
|
|
95
|
+
}
|
|
96
|
+
|
|
92
97
|
$R['runset']($basic, $subject);
|
|
93
98
|
$this->assertTrue(true, "corpus section '{$name}' completed");
|
|
94
99
|
}
|
|
@@ -111,6 +116,66 @@ class PrimaryUtilityTest extends TestCase
|
|
|
111
116
|
return ($utility->make_context)($ctxmap, $client->get_root_ctx());
|
|
112
117
|
}
|
|
113
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Where this SDK's prepare_auth actually puts the credential: the name is
|
|
121
|
+
* the API's OWN scheme name, not always `authorization`, so it is
|
|
122
|
+
* discovered by running prepare_auth once.
|
|
123
|
+
*
|
|
124
|
+
* @return array{where:string,name:string}|null
|
|
125
|
+
*/
|
|
126
|
+
private static function auth_credential(
|
|
127
|
+
ProjectNameSDK $client,
|
|
128
|
+
ProjectNameUtility $utility
|
|
129
|
+
): ?array {
|
|
130
|
+
$ctx = self::make_test_ctx($client, $utility);
|
|
131
|
+
$ctx->spec = new ProjectNameSpec(['headers' => [], 'query' => []]);
|
|
132
|
+
[, $err] = ($utility->prepare_auth)($ctx);
|
|
133
|
+
// The DEF setup this client is built from carries `auth.basic: false`,
|
|
134
|
+
// so the single-token branch runs and there is a credential to find.
|
|
135
|
+
if ($err !== null) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
foreach (['headers', 'query'] as $where) {
|
|
139
|
+
// A cookie credential rides the header bag, because a cookie IS
|
|
140
|
+
// a header.
|
|
141
|
+
$bag = 'query' === $where ? $ctx->spec->query : $ctx->spec->headers;
|
|
142
|
+
foreach ($bag as $name => $value) {
|
|
143
|
+
return ['where' => $where, 'name' => $name];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Rename the corpus's `headers` bag to the real container, and the
|
|
151
|
+
* `authorization` key inside it to the real credential name. Applied only
|
|
152
|
+
* to the prepareAuth section, so real header assertions elsewhere are
|
|
153
|
+
* untouched.
|
|
154
|
+
*
|
|
155
|
+
* @param array{where:string,name:string} $cred
|
|
156
|
+
*/
|
|
157
|
+
private static function retarget_auth($node, array $cred)
|
|
158
|
+
{
|
|
159
|
+
if (!is_array($node)) {
|
|
160
|
+
return $node;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
$out = [];
|
|
164
|
+
foreach ($node as $key => $value) {
|
|
165
|
+
if ('headers' === $key) {
|
|
166
|
+
$bag = [];
|
|
167
|
+
foreach (is_array($value) ? $value : [] as $bagkey => $bagvalue) {
|
|
168
|
+
$name = 'authorization' === $bagkey ? $cred['name'] : $bagkey;
|
|
169
|
+
$bag[$name] = self::retarget_auth($bagvalue, $cred);
|
|
170
|
+
}
|
|
171
|
+
$out[$cred['where']] = $bag;
|
|
172
|
+
} else {
|
|
173
|
+
$out[$key] = self::retarget_auth($value, $cred);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return $out;
|
|
177
|
+
}
|
|
178
|
+
|
|
114
179
|
private static function make_test_full_ctx(
|
|
115
180
|
ProjectNameSDK $client,
|
|
116
181
|
ProjectNameUtility $utility
|
|
@@ -749,6 +814,19 @@ class PrimaryUtilityTest extends TestCase
|
|
|
749
814
|
$auth_utility = $auth_client->get_utility();
|
|
750
815
|
$utility = self::client()->get_utility();
|
|
751
816
|
|
|
817
|
+
// The corpus writes the credential as `headers.authorization`: a
|
|
818
|
+
// PLACEHOLDER each runner points at the container and name this API
|
|
819
|
+
// actually uses.
|
|
820
|
+
$cred = self::auth_credential($auth_client, $auth_utility);
|
|
821
|
+
$this->assertIsArray($cred,
|
|
822
|
+
'prepare_auth placed no credential in headers or query');
|
|
823
|
+
|
|
824
|
+
$adapt = ('headers' === $cred['where'] && 'authorization' === $cred['name'])
|
|
825
|
+
? null
|
|
826
|
+
: function (array $basic) use ($cred) {
|
|
827
|
+
return self::retarget_auth($basic, $cred);
|
|
828
|
+
};
|
|
829
|
+
|
|
752
830
|
$this->runsection('prepareAuth', function (array $ctxmap) use ($auth_client, $auth_utility, $utility) {
|
|
753
831
|
$ctx = ProjectNameOmni::ctx_from_map($ctxmap, $auth_client, $auth_utility);
|
|
754
832
|
|
|
@@ -759,7 +837,7 @@ class PrimaryUtilityTest extends TestCase
|
|
|
759
837
|
throw $err;
|
|
760
838
|
}
|
|
761
839
|
return null;
|
|
762
|
-
});
|
|
840
|
+
}, $adapt);
|
|
763
841
|
}
|
|
764
842
|
|
|
765
843
|
|