@voxgig/sdkgen 1.5.0 → 2.0.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/dist/cmp/AgentGuideContent.js +5 -0
- package/dist/cmp/AgentGuideContent.js.map +1 -1
- package/dist/cmp/Feature.js +13 -0
- package/dist/cmp/Feature.js.map +1 -1
- package/dist/helpers/canonType.d.ts +3 -1
- package/dist/helpers/canonType.js +44 -1
- package/dist/helpers/canonType.js.map +1 -1
- package/dist/helpers/packageMeta.js +7 -0
- package/dist/helpers/packageMeta.js.map +1 -1
- package/dist/sdkgen.d.ts +3 -3
- package/dist/sdkgen.js +4 -1
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -6
- package/project/.sdk/model/feature/paging.aontu +7 -0
- package/project/.sdk/model/target/py-data.aontu +64 -0
- package/project/.sdk/src/cmp/c/fragment/Main.fragment.c +89 -1
- package/project/.sdk/src/cmp/clojure/Main_clojure.ts +1 -0
- package/project/.sdk/src/cmp/csharp/fragment/Main.fragment.cs +97 -0
- package/project/.sdk/src/cmp/dart/fragment/Main.fragment.dart +78 -0
- package/project/.sdk/src/cmp/elixir/fragment/Main.fragment.ex +84 -0
- package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +87 -0
- package/project/.sdk/src/cmp/js/fragment/Main.fragment.js +75 -0
- package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +85 -0
- package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +4 -0
- package/project/.sdk/src/cmp/perl/fragment/Main.fragment.pm +80 -0
- package/project/.sdk/src/cmp/php/fragment/Main.fragment.php +86 -0
- package/project/.sdk/src/cmp/py/Entity_py.ts +9 -2
- package/project/.sdk/src/cmp/py/MainEntity_py.ts +1 -1
- package/project/.sdk/src/cmp/py/Main_py.ts +34 -6
- package/project/.sdk/src/cmp/py/Package_py.ts +6 -9
- package/project/.sdk/src/cmp/py/TestDirect_py.ts +2 -2
- package/project/.sdk/src/cmp/py/TestEntity_py.ts +3 -3
- package/project/.sdk/src/cmp/py/fragment/Entity.fragment.py +2 -2
- package/project/.sdk/src/cmp/py/fragment/EntityCreateOp.fragment.py +2 -2
- package/project/.sdk/src/cmp/py/fragment/EntityLoadOp.fragment.py +2 -2
- package/project/.sdk/src/cmp/py/fragment/EntityRemoveOp.fragment.py +2 -2
- package/project/.sdk/src/cmp/py/fragment/EntityUpdateOp.fragment.py +2 -2
- package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +75 -8
- package/project/.sdk/src/cmp/py-data/Main_py-data.ts +987 -0
- package/project/.sdk/src/cmp/rb/fragment/Main.fragment.rb +71 -0
- package/project/.sdk/src/cmp/rust/fragment/Main.fragment.rs +95 -0
- package/project/.sdk/src/cmp/swift/fragment/Main.fragment.swift +86 -0
- package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +75 -0
- package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +95 -0
- package/project/.sdk/tm/c/core/sdk.h +7 -0
- package/project/.sdk/tm/c/feature/paging.c +141 -2
- package/project/.sdk/tm/c/utility/graphql.c +178 -0
- package/project/.sdk/tm/c/utility/make_options.c +1 -1
- package/project/.sdk/tm/c/utility/make_response.c +6 -0
- package/project/.sdk/tm/c/utility/make_spec.c +23 -3
- package/project/.sdk/tm/clojure/src/sdk/client.clj +64 -1
- package/project/.sdk/tm/clojure/src/sdk/core.clj +126 -3
- package/project/.sdk/tm/clojure/src/sdk/features.clj +80 -14
- package/project/.sdk/tm/cpp/core/types.hpp +83 -0
- package/project/.sdk/tm/cpp/feature/paging.hpp +103 -1
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +152 -3
- package/project/.sdk/tm/csharp/core/Utility.cs +4 -0
- package/project/.sdk/tm/csharp/feature/PagingFeature.cs +118 -1
- package/project/.sdk/tm/csharp/utility/Graphql.cs +190 -0
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +1 -1
- package/project/.sdk/tm/csharp/utility/MakeResponse.cs +7 -0
- package/project/.sdk/tm/csharp/utility/MakeSpec.cs +21 -2
- package/project/.sdk/tm/csharp/utility/Register.cs +2 -0
- package/project/.sdk/tm/dart/lib/Point.dart +7 -0
- package/project/.sdk/tm/dart/lib/feature/paging/PagingFeature.dart +99 -4
- package/project/.sdk/tm/dart/lib/utility/GraphqlUtility.dart +162 -0
- package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +1 -1
- package/project/.sdk/tm/dart/lib/utility/MakeResponseUtility.dart +5 -0
- package/project/.sdk/tm/dart/lib/utility/MakeSpecUtility.dart +18 -2
- package/project/.sdk/tm/dart/lib/utility/PrepareMethodUtility.dart +6 -1
- package/project/.sdk/tm/dart/lib/utility/Utility.dart +9 -0
- package/project/.sdk/tm/elixir/lib/projectname/feature/paging.ex +136 -13
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +187 -3
- package/project/.sdk/tm/go/core/utility_type.go +2 -0
- package/project/.sdk/tm/go/feature/paging_feature.go +87 -1
- package/project/.sdk/tm/go/utility/graphql.go +168 -0
- package/project/.sdk/tm/go/utility/make_options.go +1 -1
- package/project/.sdk/tm/go/utility/make_response.go +6 -0
- package/project/.sdk/tm/go/utility/make_spec.go +19 -2
- package/project/.sdk/tm/go/utility/register.go +2 -0
- package/project/.sdk/tm/haskell/src/SdkFeatures.hs +146 -7
- package/project/.sdk/tm/haskell/src/SdkRuntime.hs +153 -3
- package/project/.sdk/tm/java/core/SdkClient.java +93 -0
- package/project/.sdk/tm/java/core/Utility.java +4 -0
- package/project/.sdk/tm/java/feature/PagingFeature.java +100 -1
- package/project/.sdk/tm/java/utility/Graphql.java +191 -0
- package/project/.sdk/tm/java/utility/MakeOptions.java +1 -1
- package/project/.sdk/tm/java/utility/MakeResponse.java +6 -0
- package/project/.sdk/tm/java/utility/MakeSpec.java +17 -2
- package/project/.sdk/tm/java/utility/Register.java +2 -0
- package/project/.sdk/tm/js/src/Point.js +5 -0
- package/project/.sdk/tm/js/src/feature/paging/PagingFeature.js +82 -3
- package/project/.sdk/tm/js/src/feature/test/TestFeature.js +14 -2
- package/project/.sdk/tm/js/src/utility/GraphqlUtility.js +148 -0
- package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +1 -1
- package/project/.sdk/tm/js/src/utility/MakeResponseUtility.js +7 -0
- package/project/.sdk/tm/js/src/utility/MakeSpecUtility.js +19 -2
- package/project/.sdk/tm/js/src/utility/Utility.js +4 -0
- package/project/.sdk/tm/kotlin/core/SdkClient.kt +84 -0
- package/project/.sdk/tm/kotlin/core/Utility.kt +2 -0
- package/project/.sdk/tm/kotlin/feature/PagingFeature.kt +92 -1
- package/project/.sdk/tm/kotlin/utility/Graphql.kt +145 -0
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +1 -1
- package/project/.sdk/tm/kotlin/utility/MakeReqRes.kt +6 -0
- package/project/.sdk/tm/kotlin/utility/MakeSpecUrl.kt +17 -2
- package/project/.sdk/tm/kotlin/utility/Register.kt +2 -0
- package/project/.sdk/tm/lean/src/SdkUtility.lean +134 -1
- package/project/.sdk/tm/lua/feature/paging_feature.lua +97 -2
- package/project/.sdk/tm/lua/utility/graphql.lua +163 -0
- package/project/.sdk/tm/lua/utility/make_options.lua +1 -1
- package/project/.sdk/tm/lua/utility/make_response.lua +6 -0
- package/project/.sdk/tm/lua/utility/make_spec.lua +18 -2
- package/project/.sdk/tm/lua/utility/register.lua +3 -0
- package/project/.sdk/tm/ocaml/sdk_features.ml +164 -20
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +143 -4
- package/project/.sdk/tm/ocaml/sdk_types.ml +2 -0
- package/project/.sdk/tm/perl/feature/paging_feature.pm +91 -3
- package/project/.sdk/tm/perl/utility/graphql.pm +150 -0
- package/project/.sdk/tm/perl/utility/make_options.pm +1 -1
- package/project/.sdk/tm/perl/utility/make_response.pm +6 -0
- package/project/.sdk/tm/perl/utility/make_spec.pm +20 -2
- package/project/.sdk/tm/perl/utility/register.pm +1 -0
- package/project/.sdk/tm/php/core/UtilityType.php +2 -0
- package/project/.sdk/tm/php/feature/PagingFeature.php +106 -4
- package/project/.sdk/tm/php/utility/Graphql.php +176 -0
- package/project/.sdk/tm/php/utility/MakeOptions.php +1 -1
- package/project/.sdk/tm/php/utility/MakeResponse.php +7 -0
- package/project/.sdk/tm/php/utility/MakeSpec.php +20 -2
- package/project/.sdk/tm/php/utility/Register.php +3 -0
- package/project/.sdk/tm/py/pkg/core/__init__.py +0 -0
- package/project/.sdk/tm/py/{core → pkg/core}/context.py +8 -8
- package/project/.sdk/tm/py/{core → pkg/core}/operation.py +1 -1
- package/project/.sdk/tm/py/{core → pkg/core}/response.py +1 -1
- package/project/.sdk/tm/py/{core → pkg/core}/result.py +1 -1
- package/project/.sdk/tm/py/{core → pkg/core}/utility_type.py +2 -0
- package/project/.sdk/tm/py/pkg/entity/__init__.py +0 -0
- package/project/.sdk/tm/py/pkg/feature/__init__.py +0 -0
- package/project/.sdk/tm/py/{feature → pkg/feature}/audit_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/cache_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/clienttrack_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/debug_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/idempotency_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/log_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/metrics_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/netsim_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/paging_feature.py +87 -6
- package/project/.sdk/tm/py/{feature → pkg/feature}/proxy_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/ratelimit_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/rbac_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/retry_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/streaming_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/telemetry_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/test_feature.py +2 -2
- package/project/.sdk/tm/py/{feature → pkg/feature}/timeout_feature.py +1 -1
- package/project/.sdk/tm/py/pkg/utility/__init__.py +0 -0
- package/project/.sdk/tm/py/{utility → pkg/utility}/feature_init.py +1 -1
- package/project/.sdk/tm/py/{utility → pkg/utility}/fetcher.py +1 -1
- package/project/.sdk/tm/py/pkg/utility/graphql.py +145 -0
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_context.py +1 -1
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_error.py +5 -5
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_fetch_def.py +2 -2
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_options.py +2 -2
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_point.py +2 -2
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_request.py +2 -2
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_response.py +6 -0
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_spec.py +19 -4
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_url.py +1 -1
- package/project/.sdk/tm/py/{utility → pkg/utility}/param.py +2 -2
- package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_auth.py +1 -1
- package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_headers.py +1 -1
- package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_method.py +1 -1
- package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_params.py +1 -1
- package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_path.py +1 -1
- package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_query.py +1 -1
- package/project/.sdk/tm/py/pkg/utility/register.py +71 -0
- package/project/.sdk/tm/py/{utility → pkg/utility}/transform_request.py +2 -2
- package/project/.sdk/tm/py/{utility → pkg/utility}/transform_response.py +2 -2
- package/project/.sdk/tm/py/test/feature_harness.py +6 -6
- package/project/.sdk/tm/py/test/runner.py +1 -1
- package/project/.sdk/tm/py/test/struct_runner.py +1 -1
- package/project/.sdk/tm/py/test/test_pipeline.py +5 -5
- package/project/.sdk/tm/py/test/test_primary_utility.py +8 -8
- package/project/.sdk/tm/py/test/test_struct_utility.py +3 -3
- package/project/.sdk/tm/py-data/LICENSE +22 -0
- package/project/.sdk/tm/py-data/Makefile +57 -0
- package/project/.sdk/tm/py-data/pkg/auth.py +131 -0
- package/project/.sdk/tm/py-data/pkg/fetch.py +178 -0
- package/project/.sdk/tm/py-data/pkg/frames.py +236 -0
- package/project/.sdk/tm/py-data/src/feature/README.md +8 -0
- package/project/.sdk/tm/py-data/src/feature/debug/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/idempotency/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/log/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/metrics/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/netsim/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/paging/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/proxy/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/ratelimit/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/rbac/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/retry/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/streaming/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/telemetry/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/test/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/timeout/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/test/__init__.py +0 -0
- package/project/.sdk/tm/py-data/test/test_auth.py +95 -0
- package/project/.sdk/tm/py-data/test/test_fetch.py +152 -0
- package/project/.sdk/tm/py-data/test/test_frames.py +250 -0
- package/project/.sdk/tm/rb/core/utility_type.rb +1 -0
- package/project/.sdk/tm/rb/feature/paging_feature.rb +89 -3
- package/project/.sdk/tm/rb/utility/graphql.rb +131 -0
- package/project/.sdk/tm/rb/utility/make_options.rb +1 -1
- package/project/.sdk/tm/rb/utility/make_response.rb +6 -0
- package/project/.sdk/tm/rb/utility/make_spec.rb +18 -2
- package/project/.sdk/tm/rb/utility/register.rb +3 -0
- package/project/.sdk/tm/rust/feature/paging.rs +124 -2
- package/project/.sdk/tm/rust/utility/graphql.rs +193 -0
- package/project/.sdk/tm/rust/utility/make_options.rs +1 -1
- package/project/.sdk/tm/rust/utility/make_response.rs +6 -0
- package/project/.sdk/tm/rust/utility/make_spec.rs +32 -4
- package/project/.sdk/tm/rust/utility/mod.rs +1 -0
- package/project/.sdk/tm/scala/core/SdkClient.scala +90 -0
- package/project/.sdk/tm/scala/core/Utility.scala +4 -0
- package/project/.sdk/tm/scala/feature/PagingFeature.scala +104 -3
- package/project/.sdk/tm/scala/utility/Graphql.scala +160 -0
- package/project/.sdk/tm/scala/utility/Make.scala +7 -1
- package/project/.sdk/tm/scala/utility/MakeSpec.scala +22 -2
- package/project/.sdk/tm/scala/utility/Register.scala +2 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Utility.swift +4 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/PagingFeature.swift +86 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Graphql.swift +142 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Make.swift +24 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +1 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Register.swift +2 -0
- package/project/.sdk/tm/ts/src/Point.ts +7 -0
- package/project/.sdk/tm/ts/src/feature/paging/PagingFeature.ts +82 -3
- package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +14 -2
- package/project/.sdk/tm/ts/src/utility/GraphqlUtility.ts +152 -0
- package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +1 -1
- package/project/.sdk/tm/ts/src/utility/MakeResponseUtility.ts +7 -0
- package/project/.sdk/tm/ts/src/utility/MakeSpecUtility.ts +19 -2
- package/project/.sdk/tm/ts/src/utility/Utility.ts +4 -0
- package/project/.sdk/tm/zig/core/utility.zig +198 -3
- package/project/.sdk/tm/zig/feature/paging.zig +128 -1
- package/src/cmp/AgentGuideContent.ts +5 -0
- package/src/cmp/Feature.ts +13 -0
- package/src/helpers/canonType.ts +46 -0
- package/src/helpers/packageMeta.ts +7 -0
- package/src/sdkgen.ts +6 -1
- package/project/.sdk/tm/py/utility/register.py +0 -68
- /package/project/.sdk/tm/py/{core → pkg/core}/control.py +0 -0
- /package/project/.sdk/tm/py/{core → pkg/core}/error.py +0 -0
- /package/project/.sdk/tm/py/{core → pkg/core}/helpers.py +0 -0
- /package/project/.sdk/tm/py/{core → pkg/core}/spec.py +0 -0
- /package/project/.sdk/tm/py/{feature → pkg/feature}/base_feature.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/clean.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/done.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/feature_add.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/feature_hook.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/make_result.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_body.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/result_basic.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/result_body.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/result_headers.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/voxgig_struct/__init__.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/voxgig_struct/voxgig_struct.py +0 -0
- /package/project/.sdk/tm/{py/core/__init__.py → py-data/src/feature/audit/.gitkeep} +0 -0
- /package/project/.sdk/tm/{py/entity/__init__.py → py-data/src/feature/base/.gitkeep} +0 -0
- /package/project/.sdk/tm/{py/feature/__init__.py → py-data/src/feature/cache/.gitkeep} +0 -0
- /package/project/.sdk/tm/{py/utility/__init__.py → py-data/src/feature/clienttrack/.gitkeep} +0 -0
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
using System.Text.RegularExpressions;
|
|
12
12
|
|
|
13
|
+
using Voxgig.Struct;
|
|
14
|
+
|
|
13
15
|
using static ProjectNameSdk.Feature.FeatureOptions;
|
|
14
16
|
|
|
15
17
|
namespace ProjectNameSdk.Feature;
|
|
@@ -59,6 +61,16 @@ public class PagingFeature : BaseFeature
|
|
|
59
61
|
// A per-call cursor/page from ctrl takes priority (auto-iteration).
|
|
60
62
|
var paging = ctx.Ctrl?.Paging;
|
|
61
63
|
|
|
64
|
+
// GraphQL paginates through operation VARIABLES, not the query
|
|
65
|
+
// string. This hook runs after MakeSpec, so spec.Body already holds
|
|
66
|
+
// the { query, variables } envelope, and before MakeFetchDef
|
|
67
|
+
// serialises it.
|
|
68
|
+
if ("graphql" == StructUtils.GetProp(ctx.Point, "kind") as string)
|
|
69
|
+
{
|
|
70
|
+
GraphqlPreRequest(ctx, paging);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
62
74
|
object? cursor = null;
|
|
63
75
|
var hasCursor = paging != null && paging.TryGetValue("cursor", out cursor) && cursor != null;
|
|
64
76
|
if (hasCursor)
|
|
@@ -79,6 +91,60 @@ public class PagingFeature : BaseFeature
|
|
|
79
91
|
}
|
|
80
92
|
}
|
|
81
93
|
|
|
94
|
+
// Relay pagination: the cursor is the `after` variable (or whatever the
|
|
95
|
+
// model named it), and the page size is `first`.
|
|
96
|
+
private void GraphqlPreRequest(Context ctx, Dictionary<string, object?>? paging)
|
|
97
|
+
{
|
|
98
|
+
if (ctx.Spec?.Body is not Dictionary<string, object?> body)
|
|
99
|
+
{
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (body.TryGetValue("variables", out var vv) &&
|
|
104
|
+
vv is Dictionary<string, object?> existing)
|
|
105
|
+
{
|
|
106
|
+
// reuse
|
|
107
|
+
vv = existing;
|
|
108
|
+
}
|
|
109
|
+
else
|
|
110
|
+
{
|
|
111
|
+
vv = new Dictionary<string, object?>();
|
|
112
|
+
body["variables"] = vv;
|
|
113
|
+
}
|
|
114
|
+
var variables = (Dictionary<string, object?>)vv;
|
|
115
|
+
|
|
116
|
+
var afterVar = FoptStr(_options, "afterVar", "after");
|
|
117
|
+
var firstVar = FoptStr(_options, "firstVar", "first");
|
|
118
|
+
|
|
119
|
+
// Only bind variables the operation actually declares, or the server
|
|
120
|
+
// rejects the document.
|
|
121
|
+
var declared = new HashSet<string>();
|
|
122
|
+
if (StructUtils.GetPath(ctx.Point, StructUtils.Jt("graphql", "vars"))
|
|
123
|
+
is List<object?> varlist)
|
|
124
|
+
{
|
|
125
|
+
foreach (var v in varlist)
|
|
126
|
+
{
|
|
127
|
+
if (StructUtils.GetProp(v, "name") is string name)
|
|
128
|
+
{
|
|
129
|
+
declared.Add(name);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
object? cursor = null;
|
|
135
|
+
if (paging != null && paging.TryGetValue("cursor", out cursor) &&
|
|
136
|
+
cursor != null && declared.Contains(afterVar))
|
|
137
|
+
{
|
|
138
|
+
variables[afterVar] = cursor;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (Opt(_options, "limit") != null && declared.Contains(firstVar) &&
|
|
142
|
+
(!variables.TryGetValue(firstVar, out var fv) || fv == null))
|
|
143
|
+
{
|
|
144
|
+
variables[firstVar] = FoptInt(_options, "limit", 0);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
82
148
|
public override void PreResult(Context ctx)
|
|
83
149
|
{
|
|
84
150
|
if (!Active || !IsList(ctx))
|
|
@@ -113,6 +179,51 @@ public class PagingFeature : BaseFeature
|
|
|
113
179
|
}
|
|
114
180
|
}
|
|
115
181
|
|
|
182
|
+
// Set when the response states hasMore outright, rather than leaving
|
|
183
|
+
// it to be inferred from the presence of a cursor.
|
|
184
|
+
var explicitMore = false;
|
|
185
|
+
|
|
186
|
+
// Relay connections carry the cursor in pageInfo, at the path the
|
|
187
|
+
// model recorded for this op.
|
|
188
|
+
if (StructUtils.GetPath(ctx.Point, StructUtils.Jt("graphql", "page"))
|
|
189
|
+
is Dictionary<string, object?> page
|
|
190
|
+
&& body is Dictionary<string, object?>)
|
|
191
|
+
{
|
|
192
|
+
// `connpath` locates the connection object inside the response
|
|
193
|
+
// envelope (data.<field>); the cursor/more paths are relative
|
|
194
|
+
// to it.
|
|
195
|
+
object? conn = body;
|
|
196
|
+
if (page.TryGetValue("connpath", out var cpv) &&
|
|
197
|
+
cpv is string connpath && "" != connpath)
|
|
198
|
+
{
|
|
199
|
+
var sub = StructUtils.GetPath(body, connpath);
|
|
200
|
+
if (sub != null)
|
|
201
|
+
{
|
|
202
|
+
conn = sub;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (page.TryGetValue("cursor", out var curv) &&
|
|
207
|
+
curv is string cursorpath && "" != cursorpath)
|
|
208
|
+
{
|
|
209
|
+
var c = StructUtils.GetPath(conn, cursorpath);
|
|
210
|
+
if (c != null)
|
|
211
|
+
{
|
|
212
|
+
paging["cursor"] = c;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (page.TryGetValue("more", out var mv) &&
|
|
217
|
+
mv is string morepath && "" != morepath)
|
|
218
|
+
{
|
|
219
|
+
if (StructUtils.GetPath(conn, morepath) is bool more)
|
|
220
|
+
{
|
|
221
|
+
paging["hasMore"] = more;
|
|
222
|
+
explicitMore = true;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
116
227
|
// Body-level cursors.
|
|
117
228
|
if (body is Dictionary<string, object?> bm)
|
|
118
229
|
{
|
|
@@ -132,10 +243,16 @@ public class PagingFeature : BaseFeature
|
|
|
132
243
|
if (bm.TryGetValue("hasMore", out var hasMore) && hasMore is bool hmb)
|
|
133
244
|
{
|
|
134
245
|
paging["hasMore"] = hmb;
|
|
246
|
+
explicitMore = true;
|
|
135
247
|
}
|
|
136
248
|
}
|
|
137
249
|
|
|
138
|
-
|
|
250
|
+
// Cursor presence only INFERS another page. When the server stated
|
|
251
|
+
// the answer outright - relay's `hasNextPage: false`, or a body
|
|
252
|
+
// `hasMore` - that wins: a final page normally carries both an end
|
|
253
|
+
// cursor and hasNextPage false, and inferring from the cursor there
|
|
254
|
+
// would send the caller back for a page that does not exist, forever.
|
|
255
|
+
if (!explicitMore && !Equals(paging["hasMore"], true) &&
|
|
139
256
|
((paging.TryGetValue("next", out var n2) && n2 != null) ||
|
|
140
257
|
(paging.TryGetValue("cursor", out var c2) && c2 != null) ||
|
|
141
258
|
(paging.TryGetValue("nextPage", out var np2) && np2 != null)))
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// ProjectName SDK utility: graphql
|
|
2
|
+
//
|
|
3
|
+
// GraphQL transport. API-INDEPENDENT: every GraphQL SDK this generator
|
|
4
|
+
// produces uses this file unchanged. The API-specific part — which
|
|
5
|
+
// operations exist and what each one's document is — is model data,
|
|
6
|
+
// computed once by apidef and emitted into Config.
|
|
7
|
+
//
|
|
8
|
+
// Two jobs:
|
|
9
|
+
//
|
|
10
|
+
// GraphqlBodyUtil — build { query, variables } for a point, binding the
|
|
11
|
+
// op's arguments to the document's declared variables.
|
|
12
|
+
//
|
|
13
|
+
// GraphqlErrorsUtil — lift a GraphQL failure into an SDK error. GraphQL
|
|
14
|
+
// reports failures as a top-level `errors` array under
|
|
15
|
+
// HTTP 200, so the status-driven path in ResultBasic
|
|
16
|
+
// never sees them.
|
|
17
|
+
|
|
18
|
+
using Voxgig.Struct;
|
|
19
|
+
|
|
20
|
+
namespace ProjectNameSdk.Util;
|
|
21
|
+
|
|
22
|
+
public static partial class SdkUtility
|
|
23
|
+
{
|
|
24
|
+
// Content type every GraphQL-over-HTTP request uses.
|
|
25
|
+
internal const string GraphqlContentType = "application/json";
|
|
26
|
+
|
|
27
|
+
// Map a GraphQL error to the same error codes the HTTP path produces,
|
|
28
|
+
// so a caller handles auth or rate limiting identically on both
|
|
29
|
+
// transports. Servers put the machine-readable code in
|
|
30
|
+
// `extensions.code`; Linear-style APIs use `extensions.type`.
|
|
31
|
+
internal static string GraphqlErrorCode(object? gqlerr)
|
|
32
|
+
{
|
|
33
|
+
var ext = StructUtils.GetProp(gqlerr, "extensions");
|
|
34
|
+
|
|
35
|
+
var code = StructUtils.GetProp(ext, "code") as string;
|
|
36
|
+
if (string.IsNullOrEmpty(code))
|
|
37
|
+
{
|
|
38
|
+
code = StructUtils.GetProp(ext, "type") as string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
var raw = (code ?? "").ToUpperInvariant();
|
|
42
|
+
|
|
43
|
+
if (raw.Contains("AUTH") || raw.Contains("FORBIDDEN")
|
|
44
|
+
|| raw.Contains("UNAUTHENTICATED"))
|
|
45
|
+
{
|
|
46
|
+
return "request_auth";
|
|
47
|
+
}
|
|
48
|
+
if (raw.Contains("RATELIMIT") || raw.Contains("RATE_LIMIT")
|
|
49
|
+
|| raw.Contains("TOO_MANY"))
|
|
50
|
+
{
|
|
51
|
+
return "request_ratelimit";
|
|
52
|
+
}
|
|
53
|
+
if (raw.Contains("BAD_USER_INPUT") || raw.Contains("VALIDATION")
|
|
54
|
+
|| raw.Contains("INVALID"))
|
|
55
|
+
{
|
|
56
|
+
return "request_invalid";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return "request_graphql";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Build the request body for a GraphQL point.
|
|
63
|
+
//
|
|
64
|
+
// Variables come from the op's own arguments: a named variable binds to
|
|
65
|
+
// the like-named argument (`from`), and the input-object variable
|
|
66
|
+
// (empty `from`) takes the request data as a whole — which is what
|
|
67
|
+
// makes a generated create/update call look exactly like its REST
|
|
68
|
+
// equivalent.
|
|
69
|
+
internal static object? GraphqlBodyUtil(Context ctx)
|
|
70
|
+
{
|
|
71
|
+
var gql = StructUtils.GetProp(ctx.Point, "graphql")
|
|
72
|
+
as Dictionary<string, object?>;
|
|
73
|
+
if (null == gql)
|
|
74
|
+
{
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// reqmatch/reqdata hold the caller's arguments for THIS call;
|
|
79
|
+
// data/match hold the entity's current state. Which pair depends on
|
|
80
|
+
// whether the op takes match or data input. A named variable falls
|
|
81
|
+
// back to the current state, so updating a loaded entity with just
|
|
82
|
+
// {title} still binds the stored id the mutation requires.
|
|
83
|
+
var reqsrc = ctx.Reqmatch;
|
|
84
|
+
var datasrc = ctx.Match;
|
|
85
|
+
if (null != ctx.Op && "data" == ctx.Op.Input)
|
|
86
|
+
{
|
|
87
|
+
reqsrc = ctx.Reqdata;
|
|
88
|
+
datasrc = ctx.Data;
|
|
89
|
+
}
|
|
90
|
+
reqsrc ??= new Dictionary<string, object?>();
|
|
91
|
+
datasrc ??= new Dictionary<string, object?>();
|
|
92
|
+
|
|
93
|
+
var variables = new Dictionary<string, object?>();
|
|
94
|
+
|
|
95
|
+
var varlist = StructUtils.GetProp(gql, "vars") as List<object?>;
|
|
96
|
+
if (null != varlist)
|
|
97
|
+
{
|
|
98
|
+
foreach (var entry in varlist)
|
|
99
|
+
{
|
|
100
|
+
if (entry is not Dictionary<string, object?> spec)
|
|
101
|
+
{
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
var name = StructUtils.GetProp(spec, "name") as string;
|
|
106
|
+
if (null == name)
|
|
107
|
+
{
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
var from = StructUtils.GetProp(spec, "from") as string;
|
|
112
|
+
|
|
113
|
+
if (string.IsNullOrEmpty(from))
|
|
114
|
+
{
|
|
115
|
+
// The input object IS the request body. Strip the
|
|
116
|
+
// action selector, which is an SDK-side point
|
|
117
|
+
// discriminator, not an API field.
|
|
118
|
+
var body = new Dictionary<string, object?>();
|
|
119
|
+
foreach (var kv in reqsrc)
|
|
120
|
+
{
|
|
121
|
+
if ("$action" != kv.Key)
|
|
122
|
+
{
|
|
123
|
+
body[kv.Key] = kv.Value;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
variables[name] = body;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Only send variables the caller actually supplied: sending
|
|
131
|
+
// an explicit null would clear a field on many APIs.
|
|
132
|
+
var val = StructUtils.GetProp(reqsrc, from)
|
|
133
|
+
?? StructUtils.GetProp(datasrc, from);
|
|
134
|
+
if (null != val)
|
|
135
|
+
{
|
|
136
|
+
variables[name] = val;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return new Dictionary<string, object?>
|
|
142
|
+
{
|
|
143
|
+
["query"] = StructUtils.GetProp(gql, "doc"),
|
|
144
|
+
["variables"] = variables,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Inspect a decoded GraphQL response body and record a failure when the
|
|
149
|
+
// server reported one. Returns true when an error was recorded.
|
|
150
|
+
//
|
|
151
|
+
// Partial data (`data` alongside `errors`) is treated as failure: the
|
|
152
|
+
// REST surface has no partial-success concept, and silently returning
|
|
153
|
+
// half an object would be worse than failing.
|
|
154
|
+
internal static bool GraphqlErrorsUtil(Context ctx)
|
|
155
|
+
{
|
|
156
|
+
var result = ctx.Result;
|
|
157
|
+
|
|
158
|
+
if (null == result || null == ctx.Point)
|
|
159
|
+
{
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if ("graphql" != StructUtils.GetProp(ctx.Point, "kind") as string)
|
|
164
|
+
{
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (StructUtils.GetProp(result.Body, "errors") is not List<object?> errors
|
|
169
|
+
|| 0 == errors.Count)
|
|
170
|
+
{
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
var first = errors[0];
|
|
175
|
+
var msg = StructUtils.GetProp(first, "message") as string;
|
|
176
|
+
if (string.IsNullOrEmpty(msg))
|
|
177
|
+
{
|
|
178
|
+
msg = "graphql error";
|
|
179
|
+
}
|
|
180
|
+
if (1 < errors.Count)
|
|
181
|
+
{
|
|
182
|
+
msg = msg + " (+" + (errors.Count - 1) + " more)";
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
result.Err = ctx.MakeError(GraphqlErrorCode(first), "graphql: " + msg);
|
|
186
|
+
result.Ok = false;
|
|
187
|
+
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -76,7 +76,7 @@ public static partial class SdkUtility
|
|
|
76
76
|
["allow"] = new Dictionary<string, object?>
|
|
77
77
|
{
|
|
78
78
|
["method"] = "GET,PUT,POST,PATCH,DELETE,OPTIONS",
|
|
79
|
-
["op"] = "create,update,load,list,remove,command,direct",
|
|
79
|
+
["op"] = "create,update,load,list,remove,command,direct,graphql",
|
|
80
80
|
},
|
|
81
81
|
["entity"] = new Dictionary<string, object?>
|
|
82
82
|
{
|
|
@@ -38,6 +38,13 @@ public static partial class SdkUtility
|
|
|
38
38
|
utility.ResultBasic(ctx);
|
|
39
39
|
utility.ResultHeaders(ctx);
|
|
40
40
|
utility.ResultBody(ctx);
|
|
41
|
+
|
|
42
|
+
// GraphQL reports failures as a top-level `errors` array under HTTP
|
|
43
|
+
// 200, so ResultBasic's status check never sees them. Lift them
|
|
44
|
+
// here, before the response transform tries to unwrap data that is
|
|
45
|
+
// not there.
|
|
46
|
+
utility.GraphqlErrors(ctx);
|
|
47
|
+
|
|
41
48
|
utility.TransformResponse(ctx);
|
|
42
49
|
|
|
43
50
|
if (result.Err == null)
|
|
@@ -48,8 +48,27 @@ public static partial class SdkUtility
|
|
|
48
48
|
ctx.Spec.Params = utility.PrepareParams(ctx);
|
|
49
49
|
ctx.Spec.Query = utility.PrepareQuery(ctx);
|
|
50
50
|
ctx.Spec.Headers = utility.PrepareHeaders(ctx);
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
|
|
52
|
+
if ("graphql" == StructUtils.GetProp(point, "kind") as string)
|
|
53
|
+
{
|
|
54
|
+
// GraphQL addresses one endpoint: no path parts, no query
|
|
55
|
+
// string, and the body carries the operation. PrepareBody is
|
|
56
|
+
// skipped deliberately — it only emits a body for data-input
|
|
57
|
+
// ops, whereas every GraphQL op posts one, including
|
|
58
|
+
// load/list/remove.
|
|
59
|
+
ctx.Spec.Body = utility.GraphqlBody(ctx);
|
|
60
|
+
ctx.Spec.Path = "";
|
|
61
|
+
// PrepareQuery already copied the op's match arguments into the
|
|
62
|
+
// query string. Those same values are bound as operation
|
|
63
|
+
// variables, so leaving them would send /graphql?id=i1.
|
|
64
|
+
ctx.Spec.Query = new Dictionary<string, object?>();
|
|
65
|
+
ctx.Spec.Headers["content-type"] = GraphqlContentType;
|
|
66
|
+
}
|
|
67
|
+
else
|
|
68
|
+
{
|
|
69
|
+
ctx.Spec.Body = utility.PrepareBody(ctx);
|
|
70
|
+
ctx.Spec.Path = utility.PreparePath(ctx);
|
|
71
|
+
}
|
|
53
72
|
|
|
54
73
|
if (ctx.Ctrl.Explain != null)
|
|
55
74
|
{
|
|
@@ -32,6 +32,8 @@ public static partial class SdkUtility
|
|
|
32
32
|
u.PrepareParams = PrepareParamsUtil;
|
|
33
33
|
u.PreparePath = PreparePathUtil;
|
|
34
34
|
u.PrepareQuery = PrepareQueryUtil;
|
|
35
|
+
u.GraphqlBody = GraphqlBodyUtil;
|
|
36
|
+
u.GraphqlErrors = GraphqlErrorsUtil;
|
|
35
37
|
u.ResultBasic = ResultBasicUtil;
|
|
36
38
|
u.ResultBody = ResultBodyUtil;
|
|
37
39
|
u.ResultHeaders = ResultHeadersUtil;
|
|
@@ -2,6 +2,11 @@ import 'utility/voxgig_struct.dart' as vs;
|
|
|
2
2
|
|
|
3
3
|
class Point {
|
|
4
4
|
dynamic args;
|
|
5
|
+
// Transport this point speaks: 'http' (default) or 'graphql'. GraphQL
|
|
6
|
+
// points carry their operation document in `graphql` and address the
|
|
7
|
+
// single endpoint, so method is always POST and parts is empty.
|
|
8
|
+
dynamic kind;
|
|
9
|
+
dynamic graphql;
|
|
5
10
|
dynamic rename;
|
|
6
11
|
dynamic method;
|
|
7
12
|
dynamic orig;
|
|
@@ -15,6 +20,8 @@ class Point {
|
|
|
15
20
|
|
|
16
21
|
Point(dynamic altmap) {
|
|
17
22
|
args = vs.getprop(altmap, 'args', {'params': []});
|
|
23
|
+
kind = vs.getprop(altmap, 'kind', 'http');
|
|
24
|
+
graphql = vs.getprop(altmap, 'graphql');
|
|
18
25
|
rename = vs.getprop(altmap, 'rename', {'params': {}});
|
|
19
26
|
method = vs.getprop(altmap, 'method', '');
|
|
20
27
|
orig = vs.getprop(altmap, 'orig', '');
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// ignore_for_file: non_constant_identifier_names
|
|
2
2
|
|
|
3
|
+
import '../../utility/voxgig_struct.dart' as vs;
|
|
4
|
+
|
|
3
5
|
import '../base/BaseFeature.dart';
|
|
4
6
|
|
|
5
7
|
// Pagination support for list operations. On the way out (PreRequest) it
|
|
@@ -45,6 +47,14 @@ class PagingFeature extends BaseFeature {
|
|
|
45
47
|
// A per-call cursor/page from ctrl takes priority (used by auto-iteration).
|
|
46
48
|
final paging = (ctx.ctrl is Map ? ctx.ctrl['paging'] : null) ?? {};
|
|
47
49
|
|
|
50
|
+
// GraphQL paginates through operation VARIABLES, not the query string.
|
|
51
|
+
// This hook runs after makeSpec, so spec.body already holds the
|
|
52
|
+
// { query, variables } envelope, and before makeFetchDef serialises it.
|
|
53
|
+
if ('graphql' == ctx.point?.kind) {
|
|
54
|
+
_graphqlPreRequest(ctx, paging);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
48
58
|
if (null != paging['cursor']) {
|
|
49
59
|
spec.query[cursorParam] = paging['cursor'];
|
|
50
60
|
} else if (null == spec.query[pageParam]) {
|
|
@@ -58,6 +68,46 @@ class PagingFeature extends BaseFeature {
|
|
|
58
68
|
return null;
|
|
59
69
|
}
|
|
60
70
|
|
|
71
|
+
// Relay pagination: the cursor is the `after` variable (or whatever the
|
|
72
|
+
// model named it), and the page size is `first`.
|
|
73
|
+
void _graphqlPreRequest(dynamic ctx, dynamic paging) {
|
|
74
|
+
final body = ctx.spec.body;
|
|
75
|
+
if (body is! Map) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
var variables = body['variables'];
|
|
80
|
+
if (variables is! Map) {
|
|
81
|
+
variables = <String, dynamic>{};
|
|
82
|
+
body['variables'] = variables;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
final afterVar = options['afterVar'] ?? 'after';
|
|
86
|
+
final firstVar = options['firstVar'] ?? 'first';
|
|
87
|
+
|
|
88
|
+
// Only bind variables the operation actually declares, or the server
|
|
89
|
+
// rejects the document.
|
|
90
|
+
final declared = <String>{};
|
|
91
|
+
final varlist = vs.getprop(ctx.point?.graphql, 'vars');
|
|
92
|
+
if (varlist is List) {
|
|
93
|
+
for (final v in varlist) {
|
|
94
|
+
final name = vs.getprop(v, 'name');
|
|
95
|
+
if (null != name) {
|
|
96
|
+
declared.add(name.toString());
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (null != paging['cursor'] && declared.contains(afterVar)) {
|
|
102
|
+
variables[afterVar] = paging['cursor'];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (null != options['limit'] && null == variables[firstVar] &&
|
|
106
|
+
declared.contains(firstVar)) {
|
|
107
|
+
variables[firstVar] = options['limit'];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
61
111
|
@override
|
|
62
112
|
dynamic PreResult(dynamic ctx) {
|
|
63
113
|
if (!_isList(ctx)) {
|
|
@@ -90,6 +140,43 @@ class PagingFeature extends BaseFeature {
|
|
|
90
140
|
}
|
|
91
141
|
}
|
|
92
142
|
|
|
143
|
+
// Set when the response states hasMore outright, rather than leaving it
|
|
144
|
+
// to be inferred from the presence of a cursor.
|
|
145
|
+
var explicitMore = false;
|
|
146
|
+
|
|
147
|
+
// Relay connections carry the cursor in pageInfo, at the path the model
|
|
148
|
+
// recorded for this op.
|
|
149
|
+
final page = vs.getprop(ctx.point?.graphql, 'page');
|
|
150
|
+
if (page is Map && body is Map) {
|
|
151
|
+
// `connpath` locates the connection object inside the response
|
|
152
|
+
// envelope (data.<field>); the cursor/more paths are relative to it.
|
|
153
|
+
var conn = body;
|
|
154
|
+
final connpath = page['connpath'];
|
|
155
|
+
if (connpath is String && '' != connpath) {
|
|
156
|
+
final sub = vs.getpath(body, connpath);
|
|
157
|
+
if (sub is Map) {
|
|
158
|
+
conn = sub;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
final cursorpath = page['cursor'];
|
|
163
|
+
if (cursorpath is String && '' != cursorpath) {
|
|
164
|
+
final cursor = vs.getpath(conn, cursorpath);
|
|
165
|
+
if (null != cursor) {
|
|
166
|
+
paging['cursor'] = cursor;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
final morepath = page['more'];
|
|
171
|
+
if (morepath is String && '' != morepath) {
|
|
172
|
+
final more = vs.getpath(conn, morepath);
|
|
173
|
+
if (more is bool) {
|
|
174
|
+
paging['hasMore'] = more;
|
|
175
|
+
explicitMore = true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
93
180
|
// Body-level cursors.
|
|
94
181
|
if (body is Map) {
|
|
95
182
|
if (null != body['next']) {
|
|
@@ -103,13 +190,21 @@ class PagingFeature extends BaseFeature {
|
|
|
103
190
|
}
|
|
104
191
|
if (body['hasMore'] is bool) {
|
|
105
192
|
paging['hasMore'] = body['hasMore'];
|
|
193
|
+
explicitMore = true;
|
|
106
194
|
}
|
|
107
195
|
}
|
|
108
196
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
197
|
+
// Cursor presence only INFERS another page. When the server stated the
|
|
198
|
+
// answer outright — relay's `hasNextPage: false`, or a body `hasMore` —
|
|
199
|
+
// that wins: a final page normally carries both an end cursor and
|
|
200
|
+
// hasNextPage false, and inferring from the cursor there would send the
|
|
201
|
+
// caller back for a page that does not exist, forever.
|
|
202
|
+
if (!explicitMore) {
|
|
203
|
+
paging['hasMore'] = true == paging['hasMore'] ||
|
|
204
|
+
null != paging['next'] ||
|
|
205
|
+
null != paging['cursor'] ||
|
|
206
|
+
null != paging['nextPage'];
|
|
207
|
+
}
|
|
113
208
|
|
|
114
209
|
result.paging = paging;
|
|
115
210
|
|