@voxgig/sdkgen 1.5.1 → 2.0.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/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 +8 -8
- 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
|
@@ -9,10 +9,12 @@
|
|
|
9
9
|
// `cursorParam`), the page size (`limit`) and the start page (`startPage`,
|
|
10
10
|
// default 1) are configurable.
|
|
11
11
|
|
|
12
|
+
use std::cell::RefCell;
|
|
12
13
|
use std::rc::Rc;
|
|
13
14
|
|
|
14
15
|
use crate::core::context::Context;
|
|
15
|
-
use crate::core::
|
|
16
|
+
use crate::core::spec::Spec;
|
|
17
|
+
use crate::core::helpers::{get_bool, getp, getpath, setp};
|
|
16
18
|
use crate::core::types::Feature;
|
|
17
19
|
use crate::feature::support::*;
|
|
18
20
|
use crate::utility::voxgigstruct::Value;
|
|
@@ -27,6 +29,13 @@ pub struct PagingFeature {
|
|
|
27
29
|
pub last: Value,
|
|
28
30
|
}
|
|
29
31
|
|
|
32
|
+
// The model records connection/cursor/more as dot paths; core::getpath takes
|
|
33
|
+
// pre-split segments.
|
|
34
|
+
fn getpath_dotted(path: &str, store: &Value) -> Value {
|
|
35
|
+
let segs: Vec<&str> = path.split('.').collect();
|
|
36
|
+
getpath(&segs, store)
|
|
37
|
+
}
|
|
38
|
+
|
|
30
39
|
impl PagingFeature {
|
|
31
40
|
pub fn new() -> PagingFeature {
|
|
32
41
|
PagingFeature {
|
|
@@ -38,6 +47,64 @@ impl PagingFeature {
|
|
|
38
47
|
}
|
|
39
48
|
}
|
|
40
49
|
|
|
50
|
+
// Relay pagination: the cursor is the `after` variable (or whatever the
|
|
51
|
+
// model named it), and the page size is `first`.
|
|
52
|
+
fn graphql_pre_request(
|
|
53
|
+
&self, spec: &Rc<RefCell<Spec>>, point: &Value, paging: &Value,
|
|
54
|
+
) {
|
|
55
|
+
let body = spec.borrow().body.clone();
|
|
56
|
+
if !matches!(body, Value::Map(_)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let variables = {
|
|
61
|
+
let v = getp(&body, "variables");
|
|
62
|
+
if matches!(v, Value::Map(_)) {
|
|
63
|
+
v
|
|
64
|
+
} else {
|
|
65
|
+
let nv = Value::empty_map();
|
|
66
|
+
setp(&body, "variables", nv.clone());
|
|
67
|
+
nv
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
let after_var = fopt_str(&self.options, "afterVar", "after");
|
|
72
|
+
let first_var = fopt_str(&self.options, "firstVar", "first");
|
|
73
|
+
|
|
74
|
+
// Only bind variables the operation actually declares, or the server
|
|
75
|
+
// rejects the document.
|
|
76
|
+
let mut declared: Vec<String> = Vec::new();
|
|
77
|
+
if let Value::List(varlist) = getpath(&["graphql", "vars"], point) {
|
|
78
|
+
for v in varlist.borrow().iter() {
|
|
79
|
+
if let Some(name) = getp(v, "name").as_str() {
|
|
80
|
+
declared.push(name.to_string());
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let cursor = getp(paging, "cursor");
|
|
86
|
+
if !cursor.is_noval() && !cursor.is_null()
|
|
87
|
+
&& declared.iter().any(|d| *d == after_var)
|
|
88
|
+
{
|
|
89
|
+
setp(&variables, &after_var, cursor);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// `limit: null` reads as unset, matching the reference's `null !=
|
|
93
|
+
// limit`: fopt_int would otherwise coerce it to 0 and bind `first: 0`,
|
|
94
|
+
// which relay servers reject or answer with an empty page.
|
|
95
|
+
let limit = getp(&self.options, "limit");
|
|
96
|
+
if !limit.is_noval() && !limit.is_null()
|
|
97
|
+
&& getp(&variables, &first_var).is_noval()
|
|
98
|
+
&& declared.iter().any(|d| *d == first_var)
|
|
99
|
+
{
|
|
100
|
+
setp(
|
|
101
|
+
&variables,
|
|
102
|
+
&first_var,
|
|
103
|
+
Value::Num(fopt_int(&self.options, "limit", 0) as f64),
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
41
108
|
fn is_list(&self, ctx: &Rc<Context>) -> bool {
|
|
42
109
|
let opname = ctx.op.borrow().name.clone();
|
|
43
110
|
let ops =
|
|
@@ -122,6 +189,16 @@ impl Feature for PagingFeature {
|
|
|
122
189
|
// A per-call cursor/page from ctrl takes priority (auto-iteration).
|
|
123
190
|
let paging = ctx.ctrl.borrow().borrow().paging.clone();
|
|
124
191
|
|
|
192
|
+
// GraphQL paginates through operation VARIABLES, not the query
|
|
193
|
+
// string. This hook runs after make_spec, so spec.body already holds
|
|
194
|
+
// the { query, variables } envelope, and before make_fetch_def
|
|
195
|
+
// serialises it.
|
|
196
|
+
let point = ctx.point.borrow().clone();
|
|
197
|
+
if Some("graphql") == getp(&point, "kind").as_str() {
|
|
198
|
+
self.graphql_pre_request(&spec, &point, &paging);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
125
202
|
let cursor = getp(&paging, "cursor");
|
|
126
203
|
if !cursor.is_noval() && !cursor.is_null() {
|
|
127
204
|
setp(&query, &cursor_param, cursor);
|
|
@@ -176,6 +253,44 @@ impl Feature for PagingFeature {
|
|
|
176
253
|
}
|
|
177
254
|
}
|
|
178
255
|
|
|
256
|
+
// Set when the response states hasMore outright, rather than leaving
|
|
257
|
+
// it to be inferred from the presence of a cursor.
|
|
258
|
+
let mut explicit_more = false;
|
|
259
|
+
|
|
260
|
+
// Relay connections carry the cursor in pageInfo, at the path the
|
|
261
|
+
// model recorded for this op.
|
|
262
|
+
let point = ctx.point.borrow().clone();
|
|
263
|
+
let page = getpath(&["graphql", "page"], &point);
|
|
264
|
+
if matches!(page, Value::Map(_)) && matches!(body, Value::Map(_)) {
|
|
265
|
+
// `connpath` locates the connection object inside the response
|
|
266
|
+
// envelope (data.<field>); the cursor/more paths are relative
|
|
267
|
+
// to it.
|
|
268
|
+
let connpath = getp(&page, "connpath").as_str().unwrap_or("").to_string();
|
|
269
|
+
let mut conn = body.clone();
|
|
270
|
+
if !connpath.is_empty() {
|
|
271
|
+
let sub = getpath_dotted(&connpath, &body);
|
|
272
|
+
if !sub.is_noval() && !sub.is_null() {
|
|
273
|
+
conn = sub;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
let cursorpath = getp(&page, "cursor").as_str().unwrap_or("").to_string();
|
|
278
|
+
if !cursorpath.is_empty() {
|
|
279
|
+
let cursor = getpath_dotted(&cursorpath, &conn);
|
|
280
|
+
if !cursor.is_noval() && !cursor.is_null() {
|
|
281
|
+
setp(&paging, "cursor", cursor);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
let morepath = getp(&page, "more").as_str().unwrap_or("").to_string();
|
|
286
|
+
if !morepath.is_empty() {
|
|
287
|
+
if let Value::Bool(more) = getpath_dotted(&morepath, &conn) {
|
|
288
|
+
setp(&paging, "hasMore", Value::Bool(more));
|
|
289
|
+
explicit_more = true;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
179
294
|
// Body-level cursors.
|
|
180
295
|
if let Value::Map(_) = body {
|
|
181
296
|
let next = getp(&body, "next");
|
|
@@ -192,10 +307,17 @@ impl Feature for PagingFeature {
|
|
|
192
307
|
}
|
|
193
308
|
if let Some(has_more) = get_bool(&body, "hasMore") {
|
|
194
309
|
setp(&paging, "hasMore", Value::Bool(has_more));
|
|
310
|
+
explicit_more = true;
|
|
195
311
|
}
|
|
196
312
|
}
|
|
197
313
|
|
|
198
|
-
|
|
314
|
+
// Cursor presence only INFERS another page. When the server stated
|
|
315
|
+
// the answer outright — relay's `hasNextPage: false`, or a body
|
|
316
|
+
// `hasMore` — that wins: a final page normally carries both an end
|
|
317
|
+
// cursor and hasNextPage false, and inferring from the cursor there
|
|
318
|
+
// would send the caller back for a page that does not exist, forever.
|
|
319
|
+
if !explicit_more
|
|
320
|
+
&& get_bool(&paging, "hasMore") != Some(true)
|
|
199
321
|
&& (!getp(&paging, "next").is_noval()
|
|
200
322
|
|| !getp(&paging, "cursor").is_noval()
|
|
201
323
|
|| !getp(&paging, "nextPage").is_noval())
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
use std::rc::Rc;
|
|
2
|
+
|
|
3
|
+
use crate::core::context::Context;
|
|
4
|
+
use crate::core::helpers::{getp, setp};
|
|
5
|
+
use crate::utility::voxgigstruct::Value;
|
|
6
|
+
|
|
7
|
+
// GraphQL transport. API-INDEPENDENT: every GraphQL SDK this generator
|
|
8
|
+
// produces uses this file unchanged. The API-specific part — which
|
|
9
|
+
// operations exist and what each one's document is — is model data,
|
|
10
|
+
// computed once by apidef and emitted into Config.
|
|
11
|
+
//
|
|
12
|
+
// Two jobs:
|
|
13
|
+
//
|
|
14
|
+
// graphql_body_util — build { query, variables } for a point, binding
|
|
15
|
+
// the op's arguments to the document's declared
|
|
16
|
+
// variables.
|
|
17
|
+
//
|
|
18
|
+
// graphql_errors_util — lift a GraphQL failure into an SDK error.
|
|
19
|
+
// GraphQL reports failures as a top-level `errors`
|
|
20
|
+
// array under HTTP 200, so the status-driven path
|
|
21
|
+
// in result_basic never sees them.
|
|
22
|
+
|
|
23
|
+
/// Content type every GraphQL-over-HTTP request uses.
|
|
24
|
+
pub const GRAPHQL_CONTENT_TYPE: &str = "application/json";
|
|
25
|
+
|
|
26
|
+
fn as_str(v: &Value) -> String {
|
|
27
|
+
match v {
|
|
28
|
+
Value::Str(s) => s.clone(),
|
|
29
|
+
_ => String::new(),
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Map a GraphQL error to the same error codes the HTTP path produces, so
|
|
34
|
+
/// a caller handles auth or rate limiting identically on both transports.
|
|
35
|
+
/// Servers put the machine-readable code in `extensions.code`;
|
|
36
|
+
/// Linear-style APIs use `extensions.type`.
|
|
37
|
+
pub fn graphql_error_code(gqlerr: &Value) -> &'static str {
|
|
38
|
+
let ext = getp(gqlerr, "extensions");
|
|
39
|
+
|
|
40
|
+
let mut raw = as_str(&getp(&ext, "code"));
|
|
41
|
+
if raw.is_empty() {
|
|
42
|
+
raw = as_str(&getp(&ext, "type"));
|
|
43
|
+
}
|
|
44
|
+
let raw = raw.to_uppercase();
|
|
45
|
+
|
|
46
|
+
if raw.contains("AUTH") || raw.contains("FORBIDDEN")
|
|
47
|
+
|| raw.contains("UNAUTHENTICATED")
|
|
48
|
+
{
|
|
49
|
+
return "request_auth";
|
|
50
|
+
}
|
|
51
|
+
if raw.contains("RATELIMIT") || raw.contains("RATE_LIMIT")
|
|
52
|
+
|| raw.contains("TOO_MANY")
|
|
53
|
+
{
|
|
54
|
+
return "request_ratelimit";
|
|
55
|
+
}
|
|
56
|
+
if raw.contains("BAD_USER_INPUT") || raw.contains("VALIDATION")
|
|
57
|
+
|| raw.contains("INVALID")
|
|
58
|
+
{
|
|
59
|
+
return "request_invalid";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
"request_graphql"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// Build the request body for a GraphQL point.
|
|
66
|
+
///
|
|
67
|
+
/// Variables come from the op's own arguments: a named variable binds to
|
|
68
|
+
/// the like-named argument (`from`), and the input-object variable (empty
|
|
69
|
+
/// `from`) takes the request data as a whole — which is what makes a
|
|
70
|
+
/// generated create/update call look exactly like its REST equivalent.
|
|
71
|
+
pub fn graphql_body_util(ctx: &Rc<Context>) -> Value {
|
|
72
|
+
let point = ctx.point.borrow().clone();
|
|
73
|
+
let gql = getp(&point, "graphql");
|
|
74
|
+
|
|
75
|
+
if !matches!(gql, Value::Map(_)) {
|
|
76
|
+
return Value::Noval;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// reqmatch/reqdata hold the caller's arguments for THIS call; data/match
|
|
80
|
+
// hold the entity's current state. Which pair depends on whether the op
|
|
81
|
+
// takes match or data input. A named variable falls back to the current
|
|
82
|
+
// state, so updating a loaded entity with just {title} still binds the
|
|
83
|
+
// stored id the mutation requires.
|
|
84
|
+
let op = ctx.op.borrow().clone();
|
|
85
|
+
let (reqsrc, datasrc) = if op.input == "data" {
|
|
86
|
+
(ctx.reqdata.borrow().clone(), ctx.data.borrow().clone())
|
|
87
|
+
} else {
|
|
88
|
+
(ctx.reqmatch.borrow().clone(), ctx.mtch.borrow().clone())
|
|
89
|
+
};
|
|
90
|
+
let reqsrc = match reqsrc {
|
|
91
|
+
Value::Map(m) => Value::Map(m),
|
|
92
|
+
_ => Value::empty_map(),
|
|
93
|
+
};
|
|
94
|
+
let datasrc = match datasrc {
|
|
95
|
+
Value::Map(m) => Value::Map(m),
|
|
96
|
+
_ => Value::empty_map(),
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
let variables = Value::empty_map();
|
|
100
|
+
|
|
101
|
+
if let Value::List(varlist) = getp(&gql, "vars") {
|
|
102
|
+
let specs: Vec<Value> = varlist.borrow().iter().cloned().collect();
|
|
103
|
+
|
|
104
|
+
for spec in specs {
|
|
105
|
+
let name = as_str(&getp(&spec, "name"));
|
|
106
|
+
let from = as_str(&getp(&spec, "from"));
|
|
107
|
+
|
|
108
|
+
if from.is_empty() {
|
|
109
|
+
// The input object IS the request body. Strip the action
|
|
110
|
+
// selector, which is an SDK-side point discriminator, not
|
|
111
|
+
// an API field.
|
|
112
|
+
let body = Value::empty_map();
|
|
113
|
+
if let Value::Map(src) = &reqsrc {
|
|
114
|
+
let entries: Vec<(String, Value)> = src
|
|
115
|
+
.borrow()
|
|
116
|
+
.iter()
|
|
117
|
+
.map(|(k, v)| (k.clone(), v.clone()))
|
|
118
|
+
.collect();
|
|
119
|
+
for (k, v) in entries {
|
|
120
|
+
if "$action" != k {
|
|
121
|
+
setp(&body, &k, v);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
setp(&variables, &name, body);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Only send variables the caller actually supplied: sending an
|
|
130
|
+
// explicit null would clear a field on many APIs.
|
|
131
|
+
let mut val = getp(&reqsrc, &from);
|
|
132
|
+
if val.is_noval() || val.is_null() {
|
|
133
|
+
val = getp(&datasrc, &from);
|
|
134
|
+
}
|
|
135
|
+
if !val.is_noval() && !val.is_null() {
|
|
136
|
+
setp(&variables, &name, val);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let out = Value::empty_map();
|
|
142
|
+
setp(&out, "query", getp(&gql, "doc"));
|
|
143
|
+
setp(&out, "variables", variables);
|
|
144
|
+
|
|
145
|
+
out
|
|
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. The raw envelope stays
|
|
154
|
+
/// available on the result for callers that need it.
|
|
155
|
+
pub fn graphql_errors_util(ctx: &Rc<Context>) -> bool {
|
|
156
|
+
let point = ctx.point.borrow().clone();
|
|
157
|
+
|
|
158
|
+
if as_str(&getp(&point, "kind")) != "graphql" {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let result = match ctx.result.borrow().clone() {
|
|
163
|
+
Some(r) => r,
|
|
164
|
+
None => return false,
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
let body = result.borrow().body.clone();
|
|
168
|
+
let errors: Vec<Value> = match getp(&body, "errors") {
|
|
169
|
+
Value::List(l) => l.borrow().iter().cloned().collect(),
|
|
170
|
+
_ => return false,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
if errors.is_empty() {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
let first = errors[0].clone();
|
|
178
|
+
let mut msg = as_str(&getp(&first, "message"));
|
|
179
|
+
if msg.is_empty() {
|
|
180
|
+
msg = "graphql error".to_string();
|
|
181
|
+
}
|
|
182
|
+
if 1 < errors.len() {
|
|
183
|
+
msg = format!("{} (+{} more)", msg, errors.len() - 1);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
let err = ctx.make_error(graphql_error_code(&first), &format!("graphql: {}", msg));
|
|
187
|
+
|
|
188
|
+
let mut r = result.borrow_mut();
|
|
189
|
+
r.err = Some(err);
|
|
190
|
+
r.ok = false;
|
|
191
|
+
|
|
192
|
+
true
|
|
193
|
+
}
|
|
@@ -64,7 +64,7 @@ pub fn make_options_util(ctx: &Rc<Context>) -> Value {
|
|
|
64
64
|
"allow",
|
|
65
65
|
jo(vec![
|
|
66
66
|
("method", Value::str("GET,PUT,POST,PATCH,DELETE,OPTIONS")),
|
|
67
|
-
("op", Value::str("create,update,load,list,remove,command,direct")),
|
|
67
|
+
("op", Value::str("create,update,load,list,remove,command,direct,graphql")),
|
|
68
68
|
]),
|
|
69
69
|
),
|
|
70
70
|
(
|
|
@@ -35,6 +35,12 @@ pub fn make_response_util(
|
|
|
35
35
|
crate::utility::result_basic::result_basic_util(ctx);
|
|
36
36
|
crate::utility::result_headers::result_headers_util(ctx);
|
|
37
37
|
crate::utility::result_body::result_body_util(ctx);
|
|
38
|
+
|
|
39
|
+
// GraphQL reports failures as a top-level `errors` array under HTTP
|
|
40
|
+
// 200, so result_basic's status check never sees them. Lift them here,
|
|
41
|
+
// before the response transform tries to unwrap data that is not there.
|
|
42
|
+
crate::utility::graphql::graphql_errors_util(ctx);
|
|
43
|
+
|
|
38
44
|
crate::utility::transform_response::transform_response_util(ctx);
|
|
39
45
|
|
|
40
46
|
{
|
|
@@ -50,10 +50,38 @@ pub fn make_spec_util(ctx: &Rc<Context>) -> Result<Rc<RefCell<Spec>>, ProjectNam
|
|
|
50
50
|
spec.borrow_mut().query = query;
|
|
51
51
|
let headers = crate::utility::prepare_headers::prepare_headers_util(ctx);
|
|
52
52
|
spec.borrow_mut().headers = headers;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
let
|
|
56
|
-
|
|
53
|
+
|
|
54
|
+
let point = ctx.point.borrow().clone();
|
|
55
|
+
let kind = match getp(&point, "kind") {
|
|
56
|
+
Value::Str(s) => s,
|
|
57
|
+
_ => String::new(),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if "graphql" == kind {
|
|
61
|
+
// GraphQL addresses one endpoint: no path parts, no query string,
|
|
62
|
+
// and the body carries the operation. prepare_body is skipped
|
|
63
|
+
// deliberately — it only emits a body for data-input ops, whereas
|
|
64
|
+
// every GraphQL op posts one, including load/list/remove.
|
|
65
|
+
let body = crate::utility::graphql::graphql_body_util(ctx);
|
|
66
|
+
spec.borrow_mut().body = body;
|
|
67
|
+
spec.borrow_mut().path = String::new();
|
|
68
|
+
// prepare_query already copied the op's match arguments into the
|
|
69
|
+
// query string. Those same values are bound as operation
|
|
70
|
+
// variables, so leaving them would send /graphql?id=i1.
|
|
71
|
+
spec.borrow_mut().query = Value::empty_map();
|
|
72
|
+
let headers = spec.borrow().headers.clone();
|
|
73
|
+
setp(
|
|
74
|
+
&headers,
|
|
75
|
+
"content-type",
|
|
76
|
+
Value::Str(crate::utility::graphql::GRAPHQL_CONTENT_TYPE.to_string()),
|
|
77
|
+
);
|
|
78
|
+
spec.borrow_mut().headers = headers;
|
|
79
|
+
} else {
|
|
80
|
+
let body = crate::utility::prepare_body::prepare_body_util(ctx);
|
|
81
|
+
spec.borrow_mut().body = body;
|
|
82
|
+
let path = crate::utility::prepare_path::prepare_path_util(ctx);
|
|
83
|
+
spec.borrow_mut().path = path;
|
|
84
|
+
}
|
|
57
85
|
|
|
58
86
|
{
|
|
59
87
|
let ctrl = ctx.ctrl.borrow().clone();
|
|
@@ -133,7 +133,97 @@ abstract class SdkClient(options0: JMap[String, Object]) {
|
|
|
133
133
|
utility.makeFetchDef(ctx)
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
// Raw endpoint access is operator-controllable, like every entity op.
|
|
137
|
+
// Blocking it means denying BOTH the 'direct' and 'graphql' tokens, since
|
|
138
|
+
// either one reaches the same endpoint.
|
|
136
139
|
def direct(fetchargs0: JMap[String, Object]): JMap[String, Object] = {
|
|
140
|
+
if (!opAllowed("direct")) opDenied("direct")
|
|
141
|
+
else rawRequest(fetchargs0)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Is this raw-access op permitted by the SDK's allow.op option?
|
|
145
|
+
private def opAllowed(op: String): Boolean =
|
|
146
|
+
Struct.getpath(this.options, java.util.List.of("allow", "op")) match {
|
|
147
|
+
case s: String => s.contains(op)
|
|
148
|
+
case _ => false
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private def opDenied(op: String): JMap[String, Object] = {
|
|
152
|
+
val allow = Struct.getpath(this.options, java.util.List.of("allow", "op")) match {
|
|
153
|
+
case s: String => s
|
|
154
|
+
case _ => ""
|
|
155
|
+
}
|
|
156
|
+
val out = new LinkedHashMap[String, Object]()
|
|
157
|
+
out.put("ok", java.lang.Boolean.FALSE)
|
|
158
|
+
out.put("err", new SdkError(op + "_allow",
|
|
159
|
+
"ProjectNameSDK: " + op + ": operation not allowed by" +
|
|
160
|
+
" SDK option allow.op value: \"" + allow + "\"", null))
|
|
161
|
+
out
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Raw GraphQL access: the pressure valve that makes the generated surface's
|
|
165
|
+
// deliberate omissions (per-call selection sets, typed filter builders,
|
|
166
|
+
// batching, subscriptions) livable — the whole schema stays reachable.
|
|
167
|
+
//
|
|
168
|
+
// Thin wrapper over the same prepare/fetch path direct uses, with the one
|
|
169
|
+
// thing raw direct cannot do for GraphQL: a GraphQL failure rides HTTP 200
|
|
170
|
+
// as a top-level `errors` array, so status alone would report a failed
|
|
171
|
+
// query as ok.
|
|
172
|
+
//
|
|
173
|
+
// NOTE: like direct, this bypasses the feature pipeline — no retry,
|
|
174
|
+
// ratelimit or paging features apply.
|
|
175
|
+
def graphql(
|
|
176
|
+
query: String,
|
|
177
|
+
variables: JMap[String, Object] = null,
|
|
178
|
+
ctrl: JMap[String, Object] = null,
|
|
179
|
+
): JMap[String, Object] = {
|
|
180
|
+
if (!opAllowed("graphql")) opDenied("graphql")
|
|
181
|
+
else {
|
|
182
|
+
val headers = new LinkedHashMap[String, Object]()
|
|
183
|
+
headers.put("content-type", "application/json")
|
|
184
|
+
|
|
185
|
+
val body = new LinkedHashMap[String, Object]()
|
|
186
|
+
body.put("query", query)
|
|
187
|
+
body.put("variables",
|
|
188
|
+
if (variables == null) new LinkedHashMap[String, Object]() else variables)
|
|
189
|
+
|
|
190
|
+
val fetchargs = new LinkedHashMap[String, Object]()
|
|
191
|
+
fetchargs.put("method", "POST")
|
|
192
|
+
fetchargs.put("headers", headers)
|
|
193
|
+
fetchargs.put("body", body)
|
|
194
|
+
fetchargs.put("ctrl",
|
|
195
|
+
if (ctrl == null) new LinkedHashMap[String, Object]() else ctrl)
|
|
196
|
+
|
|
197
|
+
val res = rawRequest(fetchargs)
|
|
198
|
+
|
|
199
|
+
// Errors are read BEFORE any status check: a GraphQL parse or
|
|
200
|
+
// validation failure comes back as HTTP 400 carrying the standard
|
|
201
|
+
// { errors: [...] } body, and the raw path represents a non-2xx as
|
|
202
|
+
// ok:false with no err — so returning early on status would discard
|
|
203
|
+
// the server's own diagnostics, which are the only useful part of that
|
|
204
|
+
// response.
|
|
205
|
+
Struct.getpath(res, java.util.List.of("data", "errors")) match {
|
|
206
|
+
case errors: JList[_] if !errors.isEmpty =>
|
|
207
|
+
val msg = Struct.getprop(errors.get(0), "message") match {
|
|
208
|
+
case m: String if m.nonEmpty => m
|
|
209
|
+
case _ => "graphql error"
|
|
210
|
+
}
|
|
211
|
+
res.put("ok", java.lang.Boolean.FALSE)
|
|
212
|
+
res.put("err", new SdkError("graphql_error",
|
|
213
|
+
"ProjectNameSDK: graphql: " + msg, null))
|
|
214
|
+
res.put("graphql", errors.asInstanceOf[Object])
|
|
215
|
+
case _ =>
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
res
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Ungated request path shared by direct and graphql, each of which checks
|
|
223
|
+
// its own allow.op token first. Private, rather than a flag on fetchargs: a
|
|
224
|
+
// caller-supplied marker would let anyone opt straight back out of the gate
|
|
225
|
+
// by passing it.
|
|
226
|
+
private def rawRequest(fetchargs0: JMap[String, Object]): JMap[String, Object] = {
|
|
137
227
|
val utility = this.utility
|
|
138
228
|
val fetchargs = if (fetchargs0 == null) new LinkedHashMap[String, Object]() else fetchargs0
|
|
139
229
|
|
|
@@ -31,6 +31,8 @@ class Utility private (noregister: Boolean) {
|
|
|
31
31
|
var prepareParams: CtxFn[JMap[String, Object]] = null
|
|
32
32
|
var preparePath: CtxFn[String] = null
|
|
33
33
|
var prepareQuery: CtxFn[JMap[String, Object]] = null
|
|
34
|
+
var graphqlBody: CtxFn[Object] = null
|
|
35
|
+
var graphqlErrors: CtxFn[java.lang.Boolean] = null
|
|
34
36
|
var resultBasic: CtxFn[Result] = null
|
|
35
37
|
var resultBody: CtxFn[Result] = null
|
|
36
38
|
var resultHeaders: CtxFn[Result] = null
|
|
@@ -74,6 +76,8 @@ class Utility private (noregister: Boolean) {
|
|
|
74
76
|
u.prepareParams = this.prepareParams
|
|
75
77
|
u.preparePath = this.preparePath
|
|
76
78
|
u.prepareQuery = this.prepareQuery
|
|
79
|
+
u.graphqlBody = this.graphqlBody
|
|
80
|
+
u.graphqlErrors = this.graphqlErrors
|
|
77
81
|
u.resultBasic = this.resultBasic
|
|
78
82
|
u.resultBody = this.resultBody
|
|
79
83
|
u.resultHeaders = this.resultHeaders
|