@voxgig/sdkgen 1.5.1 → 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 +3 -3
- 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
|
@@ -54,8 +54,23 @@ final class MakeSpec {
|
|
|
54
54
|
ctx.spec.params = utility.prepareParams.apply(ctx);
|
|
55
55
|
ctx.spec.query = utility.prepareQuery.apply(ctx);
|
|
56
56
|
ctx.spec.headers = utility.prepareHeaders.apply(ctx);
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
|
|
58
|
+
if ("graphql".equals(Struct.getprop(point, "kind", null))) {
|
|
59
|
+
// GraphQL addresses one endpoint: no path parts, no query string, and
|
|
60
|
+
// the body carries the operation. prepareBody is skipped deliberately
|
|
61
|
+
// — it only emits a body for data-input ops, whereas every GraphQL op
|
|
62
|
+
// posts one, including load/list/remove.
|
|
63
|
+
ctx.spec.body = utility.graphqlBody.apply(ctx);
|
|
64
|
+
ctx.spec.path = "";
|
|
65
|
+
// prepareQuery already copied the op's match arguments into the query
|
|
66
|
+
// string. Those same values are bound as operation variables, so
|
|
67
|
+
// leaving them would send /graphql?id=i1.
|
|
68
|
+
ctx.spec.query = new LinkedHashMap<>();
|
|
69
|
+
ctx.spec.headers.put("content-type", Graphql.GRAPHQL_CONTENT_TYPE);
|
|
70
|
+
} else {
|
|
71
|
+
ctx.spec.body = utility.prepareBody.apply(ctx);
|
|
72
|
+
ctx.spec.path = utility.preparePath.apply(ctx);
|
|
73
|
+
}
|
|
59
74
|
|
|
60
75
|
if (ctx.ctrl.explain != null) {
|
|
61
76
|
ctx.ctrl.explain.put("spec", ctx.spec);
|
|
@@ -32,6 +32,8 @@ public final class Register {
|
|
|
32
32
|
u.prepareParams = PrepareParams::prepareParams;
|
|
33
33
|
u.preparePath = PreparePath::preparePath;
|
|
34
34
|
u.prepareQuery = PrepareQuery::prepareQuery;
|
|
35
|
+
u.graphqlBody = Graphql::graphqlBody;
|
|
36
|
+
u.graphqlErrors = Graphql::graphqlErrors;
|
|
35
37
|
u.resultBasic = ResultBasic::resultBasic;
|
|
36
38
|
u.resultBody = ResultBody::resultBody;
|
|
37
39
|
u.resultHeaders = ResultHeaders::resultHeaders;
|
|
@@ -5,6 +5,11 @@ const { getprop } = require('./utility/StructUtility')
|
|
|
5
5
|
class Point {
|
|
6
6
|
constructor(altmap) {
|
|
7
7
|
this.args = getprop(altmap, 'args', { params: [] })
|
|
8
|
+
// Transport this point speaks: 'http' (default) or 'graphql'. GraphQL
|
|
9
|
+
// points carry their operation document in `graphql` and address the
|
|
10
|
+
// single endpoint, so method is always POST and parts is empty.
|
|
11
|
+
this.kind = getprop(altmap, 'kind', 'http')
|
|
12
|
+
this.graphql = getprop(altmap, 'graphql')
|
|
8
13
|
this.rename = getprop(altmap, 'rename', { params: {} })
|
|
9
14
|
this.method = getprop(altmap, 'method', '')
|
|
10
15
|
this.orig = getprop(altmap, 'orig', '')
|
|
@@ -46,6 +46,14 @@ class PagingFeature extends BaseFeature {
|
|
|
46
46
|
// A per-call cursor/page from ctrl takes priority (used by auto-iteration).
|
|
47
47
|
const paging = (ctx.ctrl && ctx.ctrl.paging) || {}
|
|
48
48
|
|
|
49
|
+
// GraphQL paginates through operation VARIABLES, not the query string.
|
|
50
|
+
// This hook runs after makeSpec, so spec.body already holds the
|
|
51
|
+
// { query, variables } envelope, and before makeFetchDef serialises it.
|
|
52
|
+
const point = ctx.point
|
|
53
|
+
if ('graphql' === (null == point ? undefined : point.kind)) {
|
|
54
|
+
return this._graphqlPreRequest(ctx, paging)
|
|
55
|
+
}
|
|
56
|
+
|
|
49
57
|
if (null != paging.cursor) {
|
|
50
58
|
spec.query[cursorParam] = paging.cursor
|
|
51
59
|
}
|
|
@@ -71,6 +79,10 @@ class PagingFeature extends BaseFeature {
|
|
|
71
79
|
const headers = result.headers || {}
|
|
72
80
|
const body = result.body
|
|
73
81
|
|
|
82
|
+
// Set when the response states hasMore outright, rather than leaving it
|
|
83
|
+
// to be inferred from the presence of a cursor.
|
|
84
|
+
let explicitMore = false
|
|
85
|
+
|
|
74
86
|
const paging = {
|
|
75
87
|
page: this._num(this._header(headers, 'x-page')),
|
|
76
88
|
totalCount: this._num(this._header(headers, 'x-total-count')),
|
|
@@ -89,16 +101,49 @@ class PagingFeature extends BaseFeature {
|
|
|
89
101
|
}
|
|
90
102
|
}
|
|
91
103
|
|
|
104
|
+
// Relay connections carry the cursor in pageInfo, at the path the model
|
|
105
|
+
// recorded for this op.
|
|
106
|
+
const point = ctx.point
|
|
107
|
+
const page = null == point ? undefined : (point.graphql || {}).page
|
|
108
|
+
|
|
109
|
+
if (null != page && body && 'object' === typeof body) {
|
|
110
|
+
const struct = ctx.utility.struct
|
|
111
|
+
|
|
112
|
+
// `connpath` locates the connection object inside the response
|
|
113
|
+
// envelope (data.<field>); the cursor/more paths are relative to it.
|
|
114
|
+
const conn = null == page.connpath || '' === page.connpath ? body :
|
|
115
|
+
(struct.getpath(body, page.connpath) || body)
|
|
116
|
+
|
|
117
|
+
const cursor = struct.getpath(conn, page.cursor)
|
|
118
|
+
const more = struct.getpath(conn, page.more)
|
|
119
|
+
|
|
120
|
+
if (null != cursor) { paging.cursor = cursor }
|
|
121
|
+
if ('boolean' === typeof more) {
|
|
122
|
+
paging.hasMore = more
|
|
123
|
+
explicitMore = true
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
92
127
|
// Body-level cursors.
|
|
93
128
|
if (body && 'object' === typeof body) {
|
|
94
129
|
if (null != body.next) { paging.next = paging.next || body.next }
|
|
95
130
|
if (null != body.cursor) { paging.cursor = body.cursor }
|
|
96
131
|
if (null != body.nextCursor) { paging.cursor = body.nextCursor }
|
|
97
|
-
if ('boolean' === typeof body.hasMore) {
|
|
132
|
+
if ('boolean' === typeof body.hasMore) {
|
|
133
|
+
paging.hasMore = body.hasMore
|
|
134
|
+
explicitMore = true
|
|
135
|
+
}
|
|
98
136
|
}
|
|
99
137
|
|
|
100
|
-
|
|
101
|
-
|
|
138
|
+
// Cursor presence only INFERS another page. When the server stated the
|
|
139
|
+
// answer outright — relay's `hasNextPage: false`, or a body `hasMore` —
|
|
140
|
+
// that wins: a final page normally carries both an end cursor and
|
|
141
|
+
// hasNextPage false, and inferring from the cursor there would send the
|
|
142
|
+
// caller back for a page that does not exist, forever.
|
|
143
|
+
if (!explicitMore) {
|
|
144
|
+
paging.hasMore = paging.hasMore ||
|
|
145
|
+
null != paging.next || null != paging.cursor || null != paging.nextPage
|
|
146
|
+
}
|
|
102
147
|
|
|
103
148
|
result.paging = paging
|
|
104
149
|
|
|
@@ -107,6 +152,40 @@ class PagingFeature extends BaseFeature {
|
|
|
107
152
|
}
|
|
108
153
|
|
|
109
154
|
|
|
155
|
+
|
|
156
|
+
// Relay pagination: the cursor is the `after` variable (or whatever the
|
|
157
|
+
// model recorded), and the page size the `first` variable.
|
|
158
|
+
_graphqlPreRequest(ctx, paging) {
|
|
159
|
+
const spec = ctx.spec
|
|
160
|
+
const point = ctx.point
|
|
161
|
+
|
|
162
|
+
const body = spec.body
|
|
163
|
+
if (null == body || 'object' !== typeof body) {
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const vars = (body.variables = body.variables || {})
|
|
168
|
+
|
|
169
|
+
const afterVar = this._options.afterVar || 'after'
|
|
170
|
+
const firstVar = this._options.firstVar || 'first'
|
|
171
|
+
|
|
172
|
+
// Only bind variables the operation actually declares, or the server
|
|
173
|
+
// rejects the document.
|
|
174
|
+
const declared = {}
|
|
175
|
+
for (const v of ((point.graphql && point.graphql.vars) || [])) {
|
|
176
|
+
declared[v.name] = true
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (null != paging.cursor && declared[afterVar]) {
|
|
180
|
+
vars[afterVar] = paging.cursor
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (null != this._options.limit && null == vars[firstVar] &&
|
|
184
|
+
declared[firstVar]) {
|
|
185
|
+
vars[firstVar] = this._options.limit
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
110
189
|
_isList(ctx) {
|
|
111
190
|
const ops = this._options.ops || ['list']
|
|
112
191
|
return ops.indexOf(ctx.op && ctx.op.name) >= 0
|
|
@@ -61,8 +61,20 @@ class TestFeature extends BaseFeature {
|
|
|
61
61
|
if (null == data || 'string' !== typeof restf) {
|
|
62
62
|
return data
|
|
63
63
|
}
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
// Rebuild whatever nesting the op's response transform unwraps, so
|
|
65
|
+
// the mock agrees with the model. Multi-segment on purpose: GraphQL
|
|
66
|
+
// ops unwrap `body.data.<field>` (and `body.data.<field>.<entity>`
|
|
67
|
+
// for mutation payloads), not just a single envelope property.
|
|
68
|
+
const m = restf.match(/^`body\.(.+)`$/)
|
|
69
|
+
if (null == m) {
|
|
70
|
+
return data
|
|
71
|
+
}
|
|
72
|
+
let out = data
|
|
73
|
+
const segs = m[1].split('.')
|
|
74
|
+
for (let i = segs.length - 1; 0 <= i; i--) {
|
|
75
|
+
out = { [segs[i]]: out }
|
|
76
|
+
}
|
|
77
|
+
return out
|
|
66
78
|
}
|
|
67
79
|
|
|
68
80
|
function respond(status, data, res) {
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// GraphQL transport. API-INDEPENDENT: every GraphQL SDK this generator
|
|
2
|
+
// produces uses this file unchanged. The API-specific part — which
|
|
3
|
+
// operations exist and what each one's document is — is model data,
|
|
4
|
+
// computed once by apidef and emitted into Config.
|
|
5
|
+
//
|
|
6
|
+
// Two jobs:
|
|
7
|
+
//
|
|
8
|
+
// graphqlBody — build { query, variables } for a point, binding the
|
|
9
|
+
// op's arguments to the document's declared variables.
|
|
10
|
+
//
|
|
11
|
+
// graphqlErrors — lift a GraphQL failure into an SDK error. GraphQL
|
|
12
|
+
// reports failures as a top-level `errors` array under
|
|
13
|
+
// HTTP 200, so the status-driven path in resultBasic
|
|
14
|
+
// never sees them.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
// Content type every GraphQL-over-HTTP request uses.
|
|
18
|
+
const GRAPHQL_CONTENT_TYPE = 'application/json'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
// Map a GraphQL error to the same error codes the HTTP path produces, so a
|
|
22
|
+
// caller handles auth or rate limiting identically on both transports.
|
|
23
|
+
// Servers put the machine-readable code in `extensions.code`; Linear-style
|
|
24
|
+
// APIs use `extensions.type`.
|
|
25
|
+
function graphqlErrorCode(gqlerr) {
|
|
26
|
+
const ext = (null == gqlerr ? undefined : gqlerr.extensions) || {}
|
|
27
|
+
const raw = String(ext.code || ext.type || '').toUpperCase()
|
|
28
|
+
|
|
29
|
+
if (raw.includes('AUTH') || raw.includes('FORBIDDEN') ||
|
|
30
|
+
raw.includes('UNAUTHENTICATED')) {
|
|
31
|
+
return 'request_auth'
|
|
32
|
+
}
|
|
33
|
+
if (raw.includes('RATELIMIT') || raw.includes('RATE_LIMIT') ||
|
|
34
|
+
raw.includes('TOO_MANY')) {
|
|
35
|
+
return 'request_ratelimit'
|
|
36
|
+
}
|
|
37
|
+
if (raw.includes('BAD_USER_INPUT') || raw.includes('VALIDATION') ||
|
|
38
|
+
raw.includes('INVALID')) {
|
|
39
|
+
return 'request_invalid'
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return 'request_graphql'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
// Build the request body for a GraphQL point.
|
|
47
|
+
//
|
|
48
|
+
// Variables come from the op's own arguments: a named variable binds to the
|
|
49
|
+
// like-named argument (`from`), and the input-object variable (empty `from`)
|
|
50
|
+
// takes the request data as a whole — which is what makes a generated
|
|
51
|
+
// create/update call look exactly like its REST equivalent.
|
|
52
|
+
function graphqlBody(ctx) {
|
|
53
|
+
const utility = ctx.utility
|
|
54
|
+
const struct = utility.struct
|
|
55
|
+
const getprop = struct.getprop
|
|
56
|
+
|
|
57
|
+
const point = ctx.point
|
|
58
|
+
const gql = null == point ? undefined : point.graphql
|
|
59
|
+
|
|
60
|
+
if (null == gql) {
|
|
61
|
+
return undefined
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const op = ctx.op
|
|
65
|
+
const input = null == op ? 'match' : op.input
|
|
66
|
+
|
|
67
|
+
// reqmatch/reqdata hold the caller's arguments for this operation.
|
|
68
|
+
const reqsrc = getprop(ctx, 'req' + input) || {}
|
|
69
|
+
const datasrc = getprop(ctx, input) || {}
|
|
70
|
+
|
|
71
|
+
const variables = {}
|
|
72
|
+
|
|
73
|
+
for (const spec of (gql.vars || [])) {
|
|
74
|
+
if ('' === spec.from || null == spec.from) {
|
|
75
|
+
// The input object IS the request body. Strip the action selector,
|
|
76
|
+
// which is an SDK-side point discriminator, not an API field.
|
|
77
|
+
const body = {}
|
|
78
|
+
for (const key of Object.keys(reqsrc)) {
|
|
79
|
+
if ('$action' !== key) {
|
|
80
|
+
body[key] = reqsrc[key]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
variables[spec.name] = body
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
const val = undefined !== getprop(reqsrc, spec.from) ?
|
|
87
|
+
getprop(reqsrc, spec.from) : getprop(datasrc, spec.from)
|
|
88
|
+
|
|
89
|
+
// Only send variables the caller actually supplied: sending an
|
|
90
|
+
// explicit null would clear a field on many APIs.
|
|
91
|
+
if (undefined !== val && null !== val) {
|
|
92
|
+
variables[spec.name] = val
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
query: gql.doc,
|
|
99
|
+
variables,
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
// Inspect a decoded GraphQL response body and record a failure when the
|
|
105
|
+
// server reported one. Returns true when an error was recorded.
|
|
106
|
+
//
|
|
107
|
+
// Partial data (`data` alongside `errors`) is treated as failure: the REST
|
|
108
|
+
// surface has no partial-success concept, and silently returning half an
|
|
109
|
+
// object would be worse than failing. The raw envelope stays available on
|
|
110
|
+
// the result for callers that need it.
|
|
111
|
+
function graphqlErrors(ctx) {
|
|
112
|
+
const result = ctx.result
|
|
113
|
+
const point = ctx.point
|
|
114
|
+
|
|
115
|
+
if (null == result || null == point || 'graphql' !== point.kind) {
|
|
116
|
+
return false
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const body = result.body
|
|
120
|
+
const errors = null == body ? undefined : body.errors
|
|
121
|
+
|
|
122
|
+
if (null == errors || !Array.isArray(errors) || 0 === errors.length) {
|
|
123
|
+
return false
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const first = errors[0]
|
|
127
|
+
const msg = (null == first ? undefined : first.message) || 'graphql error'
|
|
128
|
+
const code = graphqlErrorCode(first)
|
|
129
|
+
|
|
130
|
+
const err = ctx.error(code,
|
|
131
|
+
'graphql: ' + msg + (1 < errors.length ?
|
|
132
|
+
' (+' + (errors.length - 1) + ' more)' : ''))
|
|
133
|
+
|
|
134
|
+
// Keep the full error list reachable for diagnostics.
|
|
135
|
+
err.graphql = errors
|
|
136
|
+
result.err = err
|
|
137
|
+
result.ok = false
|
|
138
|
+
|
|
139
|
+
return true
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
module.exports = {
|
|
144
|
+
graphqlBody,
|
|
145
|
+
graphqlErrors,
|
|
146
|
+
graphqlErrorCode,
|
|
147
|
+
GRAPHQL_CONTENT_TYPE,
|
|
148
|
+
}
|
|
@@ -10,6 +10,7 @@ async function makeResponse(ctx) {
|
|
|
10
10
|
const resultHeaders = utility.resultHeaders
|
|
11
11
|
const resultBody = utility.resultBody
|
|
12
12
|
const transformResponse = utility.transformResponse
|
|
13
|
+
const graphqlErrors = utility.graphqlErrors
|
|
13
14
|
|
|
14
15
|
const spec = ctx.spec
|
|
15
16
|
const result = ctx.result
|
|
@@ -33,6 +34,12 @@ async function makeResponse(ctx) {
|
|
|
33
34
|
resultBasic(ctx)
|
|
34
35
|
resultHeaders(ctx)
|
|
35
36
|
await resultBody(ctx)
|
|
37
|
+
|
|
38
|
+
// GraphQL reports failures as a top-level `errors` array under HTTP 200,
|
|
39
|
+
// so resultBasic's status check never sees them. Lift them here, before
|
|
40
|
+
// the response transform tries to unwrap data that is not there.
|
|
41
|
+
graphqlErrors(ctx)
|
|
42
|
+
|
|
36
43
|
transformResponse(ctx)
|
|
37
44
|
|
|
38
45
|
if (null == result.err) {
|
|
@@ -37,8 +37,25 @@ function makeSpec(ctx) {
|
|
|
37
37
|
ctx.spec.params = prepareParams(ctx)
|
|
38
38
|
ctx.spec.query = prepareQuery(ctx)
|
|
39
39
|
ctx.spec.headers = prepareHeaders(ctx)
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
|
|
41
|
+
if ('graphql' === point.kind) {
|
|
42
|
+
// GraphQL addresses one endpoint: no path parts, no query string, and
|
|
43
|
+
// the body carries the operation. prepareBody is skipped deliberately —
|
|
44
|
+
// it only emits a body for data-input ops (create/update), whereas every
|
|
45
|
+
// GraphQL op posts one, including load/list/remove.
|
|
46
|
+
ctx.spec.body = utility.graphqlBody(ctx)
|
|
47
|
+
ctx.spec.path = ''
|
|
48
|
+
// prepareQuery already copied the op's match arguments into the query
|
|
49
|
+
// string. Those same values are bound as operation variables, so leaving
|
|
50
|
+
// them would send /graphql?id=i1 — duplicating the argument, leaking it
|
|
51
|
+
// into the URL, and failing servers that reject unknown query params.
|
|
52
|
+
ctx.spec.query = {}
|
|
53
|
+
ctx.spec.headers['content-type'] = utility.GRAPHQL_CONTENT_TYPE
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
ctx.spec.body = prepareBody(ctx)
|
|
57
|
+
ctx.spec.path = preparePath(ctx)
|
|
58
|
+
}
|
|
42
59
|
|
|
43
60
|
if (ctx.ctrl.explain) {
|
|
44
61
|
ctx.ctrl.explain.spec = ctx.spec
|
|
@@ -15,6 +15,7 @@ const { makeResponse } = require('./MakeResponseUtility')
|
|
|
15
15
|
const { makeResult } = require('./MakeResultUtility')
|
|
16
16
|
const { makePoint } = require('./MakePointUtility')
|
|
17
17
|
const { makeSpec } = require('./MakeSpecUtility')
|
|
18
|
+
const { graphqlBody, graphqlErrors, GRAPHQL_CONTENT_TYPE } = require('./GraphqlUtility')
|
|
18
19
|
const { makeUrl } = require('./MakeUrlUtility')
|
|
19
20
|
const { param } = require('./ParamUtility')
|
|
20
21
|
const { prepareAuth } = require('./PrepareAuthUtility')
|
|
@@ -50,6 +51,9 @@ class Utility {
|
|
|
50
51
|
makeResult = makeResult
|
|
51
52
|
makePoint = makePoint
|
|
52
53
|
makeSpec = makeSpec
|
|
54
|
+
graphqlBody = graphqlBody
|
|
55
|
+
graphqlErrors = graphqlErrors
|
|
56
|
+
GRAPHQL_CONTENT_TYPE = GRAPHQL_CONTENT_TYPE
|
|
53
57
|
makeUrl = makeUrl
|
|
54
58
|
param = param
|
|
55
59
|
prepareAuth = prepareAuth
|
|
@@ -159,7 +159,91 @@ abstract class SdkClient(sdkopts: MutableMap<String, Any?>?) {
|
|
|
159
159
|
return utility.makeFetchDef(ctx)
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
// Raw endpoint access is operator-controllable, like every entity op.
|
|
163
|
+
// Blocking it means denying BOTH the 'direct' and 'graphql' tokens, since
|
|
164
|
+
// either one reaches the same endpoint.
|
|
162
165
|
fun direct(fetchargsIn: MutableMap<String, Any?>?): MutableMap<String, Any?> {
|
|
166
|
+
if (!opAllowed("direct")) {
|
|
167
|
+
return opDenied("direct")
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return rawRequest(fetchargsIn)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Is this raw-access op permitted by the SDK's allow.op option?
|
|
174
|
+
private fun opAllowed(op: String): Boolean {
|
|
175
|
+
val allow = Struct.getpath(this.options, listOf("allow", "op"))
|
|
176
|
+
return allow is String && allow.contains(op)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private fun opDenied(op: String): MutableMap<String, Any?> {
|
|
180
|
+
val allow = Struct.getpath(this.options, listOf("allow", "op")) as? String
|
|
181
|
+
val out = linkedMapOf<String, Any?>()
|
|
182
|
+
out["ok"] = false
|
|
183
|
+
out["err"] = SdkError(op + "_allow",
|
|
184
|
+
"ProjectNameSDK: " + op + ": operation not allowed by" +
|
|
185
|
+
" SDK option allow.op value: \"" + (allow ?: "") + "\"", null)
|
|
186
|
+
return out
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Raw GraphQL access: the pressure valve that makes the generated
|
|
190
|
+
// surface's deliberate omissions (per-call selection sets, typed filter
|
|
191
|
+
// builders, batching, subscriptions) livable — the whole schema stays
|
|
192
|
+
// reachable.
|
|
193
|
+
//
|
|
194
|
+
// Thin wrapper over the same prepare/fetch path direct uses, with the one
|
|
195
|
+
// thing raw direct cannot do for GraphQL: a GraphQL failure rides HTTP 200
|
|
196
|
+
// as a top-level `errors` array, so status alone would report a failed
|
|
197
|
+
// query as ok.
|
|
198
|
+
//
|
|
199
|
+
// NOTE: like direct, this bypasses the feature pipeline — no retry,
|
|
200
|
+
// ratelimit or paging features apply.
|
|
201
|
+
@JvmOverloads
|
|
202
|
+
fun graphql(
|
|
203
|
+
query: String,
|
|
204
|
+
variables: MutableMap<String, Any?>? = null,
|
|
205
|
+
ctrl: MutableMap<String, Any?>? = null,
|
|
206
|
+
): MutableMap<String, Any?> {
|
|
207
|
+
if (!opAllowed("graphql")) {
|
|
208
|
+
return opDenied("graphql")
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
val res = rawRequest(linkedMapOf<String, Any?>(
|
|
212
|
+
"method" to "POST",
|
|
213
|
+
"headers" to linkedMapOf<String, Any?>(
|
|
214
|
+
"content-type" to "application/json"),
|
|
215
|
+
"body" to linkedMapOf<String, Any?>(
|
|
216
|
+
"query" to query,
|
|
217
|
+
"variables" to (variables ?: linkedMapOf<String, Any?>())),
|
|
218
|
+
"ctrl" to (ctrl ?: linkedMapOf<String, Any?>()),
|
|
219
|
+
))
|
|
220
|
+
|
|
221
|
+
// Errors are read BEFORE any status check: a GraphQL parse or validation
|
|
222
|
+
// failure comes back as HTTP 400 carrying the standard { errors: [...] }
|
|
223
|
+
// body, and the raw path represents a non-2xx as ok:false with no err —
|
|
224
|
+
// so returning early on status would discard the server's own
|
|
225
|
+
// diagnostics, which are the only useful part of that response.
|
|
226
|
+
val errors = Struct.getpath(res, listOf("data", "errors")) as? List<Any?>
|
|
227
|
+
|
|
228
|
+
if (null != errors && errors.isNotEmpty()) {
|
|
229
|
+
val m = Struct.getprop(errors[0], "message") as? String
|
|
230
|
+
val msg = if (m.isNullOrEmpty()) "graphql error" else m
|
|
231
|
+
res["ok"] = false
|
|
232
|
+
res["err"] = SdkError("graphql_error",
|
|
233
|
+
"ProjectNameSDK: graphql: " + msg, null)
|
|
234
|
+
res["graphql"] = errors
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return res
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Ungated request path shared by direct and graphql, each of which checks
|
|
241
|
+
// its own allow.op token first. Private, rather than a flag on fetchargs: a
|
|
242
|
+
// caller-supplied marker would let anyone opt straight back out of the gate
|
|
243
|
+
// by passing it.
|
|
244
|
+
private fun rawRequest(
|
|
245
|
+
fetchargsIn: MutableMap<String, Any?>?,
|
|
246
|
+
): MutableMap<String, Any?> {
|
|
163
247
|
val utility = this.util
|
|
164
248
|
|
|
165
249
|
val fetchargs = fetchargsIn ?: linkedMapOf()
|
|
@@ -37,6 +37,8 @@ class Utility private constructor(register: Boolean) {
|
|
|
37
37
|
lateinit var prepareParams: (Context) -> MutableMap<String, Any?>
|
|
38
38
|
lateinit var preparePath: (Context) -> String
|
|
39
39
|
lateinit var prepareQuery: (Context) -> MutableMap<String, Any?>
|
|
40
|
+
lateinit var graphqlBody: (Context) -> Any?
|
|
41
|
+
lateinit var graphqlErrors: (Context) -> Boolean
|
|
40
42
|
lateinit var resultBasic: (Context) -> Result
|
|
41
43
|
lateinit var resultBody: (Context) -> Result
|
|
42
44
|
lateinit var resultHeaders: (Context) -> Result
|
|
@@ -4,6 +4,7 @@ import java.util.regex.Pattern
|
|
|
4
4
|
|
|
5
5
|
import KOTLINPACKAGE.core.Context
|
|
6
6
|
import KOTLINPACKAGE.core.SdkClient
|
|
7
|
+
import KOTLINPACKAGE.utility.struct.Struct
|
|
7
8
|
|
|
8
9
|
// Pagination support for list operations. On the way out (PreRequest) it
|
|
9
10
|
// stamps page/limit (or a cursor) into the request query; on the way back
|
|
@@ -40,6 +41,14 @@ class PagingFeature : BaseFeature("paging", "0.0.1", true) {
|
|
|
40
41
|
paging = linkedMapOf()
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
// GraphQL paginates through operation VARIABLES, not the query string.
|
|
45
|
+
// This hook runs after makeSpec, so spec.body already holds the
|
|
46
|
+
// { query, variables } envelope, and before makeFetchDef serialises it.
|
|
47
|
+
if ("graphql" == Struct.getprop(ctx.point, "kind") as? String) {
|
|
48
|
+
graphqlPreRequest(ctx, paging)
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
|
|
43
52
|
val cursor = paging["cursor"]
|
|
44
53
|
if (cursor != null) {
|
|
45
54
|
spec.query[cursorParam] = cursor
|
|
@@ -57,6 +66,45 @@ class PagingFeature : BaseFeature("paging", "0.0.1", true) {
|
|
|
57
66
|
}
|
|
58
67
|
}
|
|
59
68
|
|
|
69
|
+
// Relay pagination: the cursor is the `after` variable (or whatever the
|
|
70
|
+
// model named it), and the page size is `first`.
|
|
71
|
+
private fun graphqlPreRequest(ctx: Context, paging: MutableMap<String, Any?>) {
|
|
72
|
+
val body = ctx.spec?.body as? MutableMap<String, Any?> ?: return
|
|
73
|
+
|
|
74
|
+
var variables = body["variables"] as? MutableMap<String, Any?>
|
|
75
|
+
if (variables == null) {
|
|
76
|
+
variables = linkedMapOf()
|
|
77
|
+
body["variables"] = variables
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
val afterVar = FeatureOptions.foptStr(this.options, "afterVar", "after")
|
|
81
|
+
val firstVar = FeatureOptions.foptStr(this.options, "firstVar", "first")
|
|
82
|
+
|
|
83
|
+
// Only bind variables the operation actually declares, or the server
|
|
84
|
+
// rejects the document.
|
|
85
|
+
val declared = mutableSetOf<String>()
|
|
86
|
+
val varlist = Struct.getpath(ctx.point, listOf("graphql", "vars")) as? List<Any?>
|
|
87
|
+
if (varlist != null) {
|
|
88
|
+
for (v in varlist) {
|
|
89
|
+
val name = Struct.getprop(v, "name") as? String
|
|
90
|
+
if (name != null) {
|
|
91
|
+
declared.add(name)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
val cursor = paging["cursor"]
|
|
97
|
+
if (cursor != null && declared.contains(afterVar)) {
|
|
98
|
+
variables[afterVar] = cursor
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (this.options?.get("limit") != null && variables[firstVar] == null &&
|
|
102
|
+
declared.contains(firstVar)
|
|
103
|
+
) {
|
|
104
|
+
variables[firstVar] = FeatureOptions.foptInt(this.options, "limit", 0)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
60
108
|
override fun preResult(ctx: Context) {
|
|
61
109
|
if (!this.active || !isList(ctx)) {
|
|
62
110
|
return
|
|
@@ -81,6 +129,43 @@ class PagingFeature : BaseFeature("paging", "0.0.1", true) {
|
|
|
81
129
|
}
|
|
82
130
|
}
|
|
83
131
|
|
|
132
|
+
// Set when the response states hasMore outright, rather than leaving it
|
|
133
|
+
// to be inferred from the presence of a cursor.
|
|
134
|
+
var explicitMore = false
|
|
135
|
+
|
|
136
|
+
// Relay connections carry the cursor in pageInfo, at the path the model
|
|
137
|
+
// recorded for this op.
|
|
138
|
+
val page = Struct.getpath(ctx.point, listOf("graphql", "page")) as? Map<*, *>
|
|
139
|
+
if (page != null && body is MutableMap<*, *>) {
|
|
140
|
+
// `connpath` locates the connection object inside the response
|
|
141
|
+
// envelope (data.<field>); the cursor/more paths are relative to it.
|
|
142
|
+
var conn: Any? = body
|
|
143
|
+
val connpath = page["connpath"] as? String
|
|
144
|
+
if (!connpath.isNullOrEmpty()) {
|
|
145
|
+
val sub = Struct.getpath(body, connpath)
|
|
146
|
+
if (sub != null) {
|
|
147
|
+
conn = sub
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
val cursorpath = page["cursor"] as? String
|
|
152
|
+
if (!cursorpath.isNullOrEmpty()) {
|
|
153
|
+
val c = Struct.getpath(conn, cursorpath)
|
|
154
|
+
if (c != null) {
|
|
155
|
+
paging["cursor"] = c
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
val morepath = page["more"] as? String
|
|
160
|
+
if (!morepath.isNullOrEmpty()) {
|
|
161
|
+
val more = Struct.getpath(conn, morepath)
|
|
162
|
+
if (more is Boolean) {
|
|
163
|
+
paging["hasMore"] = more
|
|
164
|
+
explicitMore = true
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
84
169
|
// Body-level cursors.
|
|
85
170
|
if (body is MutableMap<*, *>) {
|
|
86
171
|
val bm = body as MutableMap<String, Any?>
|
|
@@ -95,10 +180,16 @@ class PagingFeature : BaseFeature("paging", "0.0.1", true) {
|
|
|
95
180
|
}
|
|
96
181
|
if (bm["hasMore"] is Boolean) {
|
|
97
182
|
paging["hasMore"] = bm["hasMore"]
|
|
183
|
+
explicitMore = true
|
|
98
184
|
}
|
|
99
185
|
}
|
|
100
186
|
|
|
101
|
-
|
|
187
|
+
// Cursor presence only INFERS another page. When the server stated the
|
|
188
|
+
// answer outright — relay's `hasNextPage: false`, or a body `hasMore` —
|
|
189
|
+
// that wins: a final page normally carries both an end cursor and
|
|
190
|
+
// hasNextPage false, and inferring from the cursor there would send the
|
|
191
|
+
// caller back for a page that does not exist, forever.
|
|
192
|
+
if (!explicitMore && paging["hasMore"] != true &&
|
|
102
193
|
(paging["next"] != null || paging["cursor"] != null || paging["nextPage"] != null)
|
|
103
194
|
) {
|
|
104
195
|
paging["hasMore"] = true
|