@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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
-- ProjectName SDK utility: make_spec
|
|
2
2
|
|
|
3
3
|
local vs = require("utility.struct.struct")
|
|
4
|
+
local graphql = require("utility.graphql")
|
|
4
5
|
local Spec = require("core.spec")
|
|
5
6
|
|
|
6
7
|
local function make_spec_util(ctx)
|
|
@@ -53,8 +54,23 @@ local function make_spec_util(ctx)
|
|
|
53
54
|
ctx.spec.params = utility.prepare_params(ctx)
|
|
54
55
|
ctx.spec.query = utility.prepare_query(ctx)
|
|
55
56
|
ctx.spec.headers = utility.prepare_headers(ctx)
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
|
|
58
|
+
if "graphql" == vs.getprop(ctx.point, "kind") then
|
|
59
|
+
-- GraphQL addresses one endpoint: no path parts, no query string, and
|
|
60
|
+
-- the body carries the operation. prepare_body 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.graphql_body(ctx)
|
|
64
|
+
ctx.spec.path = ""
|
|
65
|
+
-- prepare_query 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 = {}
|
|
69
|
+
ctx.spec.headers["content-type"] = graphql.CONTENT_TYPE
|
|
70
|
+
else
|
|
71
|
+
ctx.spec.body = utility.prepare_body(ctx)
|
|
72
|
+
ctx.spec.path = utility.prepare_path(ctx)
|
|
73
|
+
end
|
|
58
74
|
|
|
59
75
|
if ctx.ctrl.explain ~= nil then
|
|
60
76
|
ctx.ctrl.explain["spec"] = ctx.spec
|
|
@@ -26,6 +26,7 @@ local prepare_method = require("utility.prepare_method")
|
|
|
26
26
|
local prepare_params = require("utility.prepare_params")
|
|
27
27
|
local prepare_path = require("utility.prepare_path")
|
|
28
28
|
local prepare_query = require("utility.prepare_query")
|
|
29
|
+
local graphql = require("utility.graphql")
|
|
29
30
|
local result_basic = require("utility.result_basic")
|
|
30
31
|
local result_body = require("utility.result_body")
|
|
31
32
|
local result_headers = require("utility.result_headers")
|
|
@@ -58,6 +59,8 @@ local function register_all(u)
|
|
|
58
59
|
u.prepare_params = prepare_params
|
|
59
60
|
u.prepare_path = prepare_path
|
|
60
61
|
u.prepare_query = prepare_query
|
|
62
|
+
u.graphql_body = graphql.body
|
|
63
|
+
u.graphql_errors = graphql.errors
|
|
61
64
|
u.result_basic = result_basic
|
|
62
65
|
u.result_body = result_body
|
|
63
66
|
u.result_headers = result_headers
|
|
@@ -643,6 +643,37 @@ let clienttrack_feature () : feature =
|
|
|
643
643
|
(* paging *)
|
|
644
644
|
(* ------------------------------------------------------------------ *)
|
|
645
645
|
|
|
646
|
+
(* Relay pagination: the cursor is the `after` variable (or whatever the model
|
|
647
|
+
* named it), and the page size is `first`. *)
|
|
648
|
+
let graphql_pre_request (options : value) (spec : spec) (point : value)
|
|
649
|
+
(paging : value) : unit =
|
|
650
|
+
match spec.sp_body with
|
|
651
|
+
| Map _ as body ->
|
|
652
|
+
let variables = match getp body "variables" with
|
|
653
|
+
| Map _ as m -> m
|
|
654
|
+
| _ -> let m = empty_map () in setp body "variables" m; m in
|
|
655
|
+
|
|
656
|
+
let after_var = opt_str options "afterVar" ~default:"after" in
|
|
657
|
+
let first_var = opt_str options "firstVar" ~default:"first" in
|
|
658
|
+
|
|
659
|
+
(* Only bind variables the operation actually declares, or the server
|
|
660
|
+
* rejects the document. *)
|
|
661
|
+
let declared = match getpath_s point "graphql.vars" with
|
|
662
|
+
| List r -> List.filter_map (fun v -> match getp v "name" with
|
|
663
|
+
| Str n -> Some n | _ -> None) !r
|
|
664
|
+
| _ -> [] in
|
|
665
|
+
|
|
666
|
+
(match getp paging "cursor" with
|
|
667
|
+
| Noval | Null -> ()
|
|
668
|
+
| c -> if List.mem after_var declared then setp variables after_var c);
|
|
669
|
+
|
|
670
|
+
(match getp options "limit" with
|
|
671
|
+
| Noval | Null -> ()
|
|
672
|
+
| lim ->
|
|
673
|
+
if List.mem first_var declared && is_nullish (getp variables first_var)
|
|
674
|
+
then setp variables first_var lim)
|
|
675
|
+
| _ -> ()
|
|
676
|
+
|
|
646
677
|
let paging_feature () : feature =
|
|
647
678
|
let options = ref (empty_map ()) in
|
|
648
679
|
let f = { f_name = "paging"; f_version = "0.0.1"; f_active = true; f_options = Noval;
|
|
@@ -676,19 +707,27 @@ let paging_feature () : feature =
|
|
|
676
707
|
let limit_param = opt_str !options "limitParam" ~default:"limit" in
|
|
677
708
|
let cursor_param = opt_str !options "cursorParam" ~default:"cursor" in
|
|
678
709
|
let paging = match ctx.c_ctrl.ctrl_paging with Map _ as m -> m | _ -> empty_map () in
|
|
679
|
-
(
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
710
|
+
(* GraphQL paginates through operation VARIABLES, not the query
|
|
711
|
+
* string. This hook runs after make_spec, so spec.sp_body already
|
|
712
|
+
* holds the { query, variables } envelope, and before
|
|
713
|
+
* make_fetch_def serialises it. *)
|
|
714
|
+
if get_str_d ctx.c_point "kind" "" = "graphql" then
|
|
715
|
+
graphql_pre_request !options spec ctx.c_point paging
|
|
716
|
+
else begin
|
|
717
|
+
(match getp paging "cursor" with
|
|
718
|
+
| Noval | Null ->
|
|
719
|
+
(match getp q page_param with
|
|
720
|
+
| Noval ->
|
|
721
|
+
let page = (match getp paging "page" with
|
|
722
|
+
| Noval | Null -> (match getp !options "startPage" with Num n -> Num n | _ -> Num 1.)
|
|
723
|
+
| p -> p) in
|
|
724
|
+
setp q page_param page
|
|
725
|
+
| _ -> ())
|
|
726
|
+
| c -> setp q cursor_param c);
|
|
727
|
+
(match getp !options "limit" with
|
|
728
|
+
| Noval | Null -> ()
|
|
729
|
+
| lim -> (match getp q limit_param with Noval -> setp q limit_param lim | _ -> ()))
|
|
730
|
+
end)
|
|
692
731
|
| "PreResult" ->
|
|
693
732
|
(match ctx.c_result with
|
|
694
733
|
| None -> ()
|
|
@@ -703,18 +742,56 @@ let paging_feature () : feature =
|
|
|
703
742
|
(match header_ci headers "link" with
|
|
704
743
|
| Noval | Null -> ()
|
|
705
744
|
| l -> (match extract_next (vstr_of l) with Some n -> setp paging "next" (Str n) | None -> ()));
|
|
745
|
+
(* Relay connections carry the cursor in pageInfo, at the path
|
|
746
|
+
* the model recorded for this op. `explicit_more` is set when
|
|
747
|
+
* the server states hasMore outright, rather than leaving it to
|
|
748
|
+
* be inferred from the presence of a cursor. *)
|
|
749
|
+
let explicit_more = ref false in
|
|
750
|
+
let gqlpage = getpath_s ctx.c_point "graphql.page" in
|
|
751
|
+
(match gqlpage, body with
|
|
752
|
+
| Map _, Map _ ->
|
|
753
|
+
(* `connpath` locates the connection object inside the
|
|
754
|
+
* response envelope (data.<field>); the cursor/more paths are
|
|
755
|
+
* relative to it. *)
|
|
756
|
+
let connpath = get_str_d gqlpage "connpath" "" in
|
|
757
|
+
let conn =
|
|
758
|
+
if connpath = "" then body
|
|
759
|
+
else (match getpath_s body connpath with
|
|
760
|
+
| Noval | Null -> body
|
|
761
|
+
| sub -> sub) in
|
|
762
|
+
let cursorpath = get_str_d gqlpage "cursor" "" in
|
|
763
|
+
if cursorpath <> "" then
|
|
764
|
+
(match getpath_s conn cursorpath with
|
|
765
|
+
| Noval | Null -> ()
|
|
766
|
+
| c -> setp paging "cursor" c);
|
|
767
|
+
let morepath = get_str_d gqlpage "more" "" in
|
|
768
|
+
if morepath <> "" then
|
|
769
|
+
(match getpath_s conn morepath with
|
|
770
|
+
| Bool b -> setp paging "hasMore" (Bool b); explicit_more := true
|
|
771
|
+
| _ -> ())
|
|
772
|
+
| _ -> ());
|
|
706
773
|
(match body with
|
|
707
774
|
| Map _ ->
|
|
708
775
|
(match getp body "next" with Noval | Null -> () | n -> if is_nullish (getp paging "next") then setp paging "next" n);
|
|
709
776
|
(match getp body "cursor" with Noval | Null -> () | c -> setp paging "cursor" c);
|
|
710
777
|
(match getp body "nextCursor" with Noval | Null -> () | c -> setp paging "cursor" c);
|
|
711
|
-
(match getp body "hasMore" with
|
|
778
|
+
(match getp body "hasMore" with
|
|
779
|
+
| Bool b -> setp paging "hasMore" (Bool b); explicit_more := true
|
|
780
|
+
| _ -> ())
|
|
712
781
|
| _ -> ());
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
782
|
+
(* Cursor presence only INFERS another page. When the server
|
|
783
|
+
* stated the answer outright — relay's `hasNextPage: false`, or
|
|
784
|
+
* a body `hasMore` — that wins: a final page normally carries
|
|
785
|
+
* both an end cursor and hasNextPage false, and inferring from
|
|
786
|
+
* the cursor there would send the caller back for a page that
|
|
787
|
+
* does not exist, forever. *)
|
|
788
|
+
if not !explicit_more then begin
|
|
789
|
+
let hm = (getp paging "hasMore" = Bool true)
|
|
790
|
+
|| not (is_nullish (getp paging "next"))
|
|
791
|
+
|| not (is_nullish (getp paging "cursor"))
|
|
792
|
+
|| not (is_nullish (getp paging "nextPage")) in
|
|
793
|
+
setp paging "hasMore" (Bool hm)
|
|
794
|
+
end;
|
|
718
795
|
result.rt_paging <- paging;
|
|
719
796
|
track_set (cc ctx) "paging" (jo [("last", paging)]))
|
|
720
797
|
| _ -> ());
|
|
@@ -1090,7 +1167,24 @@ let prepare (client : sdk_client) (fetchargs : value) : value =
|
|
|
1090
1167
|
(match u.u_prepare_auth ctx with (_, Some err) -> raise (Sdk_error_exc err) | _ -> ());
|
|
1091
1168
|
(match u.u_make_fetch_def ctx with (_, Some err) -> raise (Sdk_error_exc err) | (fd, None) -> fd)
|
|
1092
1169
|
|
|
1093
|
-
|
|
1170
|
+
(* Is this raw-access op permitted by the SDK's allow.op option? *)
|
|
1171
|
+
let op_allowed (client : sdk_client) (op : string) : bool =
|
|
1172
|
+
match getpath_s client.cl_options "allow.op" with
|
|
1173
|
+
| Str allow -> substr_contains allow op
|
|
1174
|
+
| _ -> false
|
|
1175
|
+
|
|
1176
|
+
let op_denied (client : sdk_client) (op : string) : value =
|
|
1177
|
+
let allow = match getpath_s client.cl_options "allow.op" with
|
|
1178
|
+
| Str s -> s | _ -> "" in
|
|
1179
|
+
jo [("ok", Bool false);
|
|
1180
|
+
("err", Str ("ProjectNameSDK: " ^ op ^ ": operation not allowed by" ^
|
|
1181
|
+
" SDK option allow.op value: \"" ^ allow ^ "\""))]
|
|
1182
|
+
|
|
1183
|
+
(* Ungated request path shared by direct and graphql, each of which checks its
|
|
1184
|
+
* own allow.op token first. Separate, rather than a flag on fetchargs: a
|
|
1185
|
+
* caller-supplied marker would let anyone opt straight back out of the gate
|
|
1186
|
+
* by passing it. *)
|
|
1187
|
+
let raw_request (client : sdk_client) (fetchargs : value) : value =
|
|
1094
1188
|
let u = client.cl_utility in
|
|
1095
1189
|
let fetchargs = if is_noval fetchargs then empty_map () else fetchargs in
|
|
1096
1190
|
match (try `Ok (prepare client fetchargs) with Sdk_error_exc e -> `Err e) with
|
|
@@ -1117,6 +1211,56 @@ let direct (client : sdk_client) (fetchargs : value) : value =
|
|
|
1117
1211
|
("headers", headers); ("data", json_data)]
|
|
1118
1212
|
| _ -> jo [("ok", Bool false); ("err", err_to_value (ctx_make_error ctx "direct_invalid" "invalid response type"))]))
|
|
1119
1213
|
|
|
1214
|
+
(* Raw endpoint access is operator-controllable, like every entity op.
|
|
1215
|
+
* Blocking it means denying BOTH the 'direct' and 'graphql' tokens, since
|
|
1216
|
+
* either one reaches the same endpoint. *)
|
|
1217
|
+
let direct (client : sdk_client) (fetchargs : value) : value =
|
|
1218
|
+
if not (op_allowed client "direct") then op_denied client "direct"
|
|
1219
|
+
else raw_request client fetchargs
|
|
1220
|
+
|
|
1221
|
+
(* Raw GraphQL access: the pressure valve that makes the generated surface's
|
|
1222
|
+
* deliberate omissions (per-call selection sets, typed filter builders,
|
|
1223
|
+
* batching, subscriptions) livable — the whole schema stays reachable.
|
|
1224
|
+
*
|
|
1225
|
+
* Thin wrapper over the same prepare/fetch path direct uses, with the one
|
|
1226
|
+
* thing raw direct cannot do for GraphQL: a GraphQL failure rides HTTP 200 as
|
|
1227
|
+
* a top-level `errors` array, so status alone would report a failed query as
|
|
1228
|
+
* ok.
|
|
1229
|
+
*
|
|
1230
|
+
* NOTE: like direct, this bypasses the feature pipeline — no retry, ratelimit
|
|
1231
|
+
* or paging features apply. *)
|
|
1232
|
+
let graphql (client : sdk_client) (query : string) (variables : value)
|
|
1233
|
+
(ctrl : value) : value =
|
|
1234
|
+
if not (op_allowed client "graphql") then op_denied client "graphql"
|
|
1235
|
+
else begin
|
|
1236
|
+
let vars = match variables with Map _ as m -> m | _ -> empty_map () in
|
|
1237
|
+
let ctl = match ctrl with Map _ as m -> m | _ -> empty_map () in
|
|
1238
|
+
let res = raw_request client (jo [
|
|
1239
|
+
("method", Str "POST");
|
|
1240
|
+
("headers", jo [("content-type", Str "application/json")]);
|
|
1241
|
+
("body", jo [("query", Str query); ("variables", vars)]);
|
|
1242
|
+
("ctrl", ctl)]) in
|
|
1243
|
+
|
|
1244
|
+
(* Errors are read BEFORE any status check: a GraphQL parse or validation
|
|
1245
|
+
* failure comes back as HTTP 400 carrying the standard { errors: [...] }
|
|
1246
|
+
* body, and the raw path represents a non-2xx as ok:false with no err —
|
|
1247
|
+
* so returning early on status would discard the server's own
|
|
1248
|
+
* diagnostics, which are the only useful part of that response. *)
|
|
1249
|
+
let errors = match getpath_s res "data.errors" with
|
|
1250
|
+
| List r -> !r | _ -> [] in
|
|
1251
|
+
|
|
1252
|
+
(match errors with
|
|
1253
|
+
| [] -> ()
|
|
1254
|
+
| first :: _ ->
|
|
1255
|
+
let msg = get_str_d first "message" "" in
|
|
1256
|
+
let msg = if msg = "" then "graphql error" else msg in
|
|
1257
|
+
setp res "ok" (Bool false);
|
|
1258
|
+
setp res "err" (Str ("ProjectNameSDK: graphql: " ^ msg));
|
|
1259
|
+
setp res "graphql" (lst errors));
|
|
1260
|
+
|
|
1261
|
+
res
|
|
1262
|
+
end
|
|
1263
|
+
|
|
1120
1264
|
let sdk_test ~(config : value) ~(make_feature : string -> feature) (testopts : value) (sdkopts : value) : sdk_client =
|
|
1121
1265
|
let sdkopts = match clone (if is_noval sdkopts then empty_map () else sdkopts) with Map _ as m -> m | _ -> empty_map () in
|
|
1122
1266
|
let testopts = match clone (if is_noval testopts then empty_map () else testopts) with Map _ as m -> m | _ -> empty_map () in
|
|
@@ -416,6 +416,120 @@ let prepare_query_util (ctx : ctx) : value =
|
|
|
416
416
|
let prepare_body_util (ctx : ctx) : value =
|
|
417
417
|
if ctx.c_op.op_input = "data" then (cu ctx).u_transform_request ctx else Noval
|
|
418
418
|
|
|
419
|
+
(* ---- graphql (transport) -------------------------------------------------
|
|
420
|
+
*
|
|
421
|
+
* GraphQL transport. API-INDEPENDENT: every GraphQL SDK this generator
|
|
422
|
+
* produces uses this code unchanged. The API-specific part — which operations
|
|
423
|
+
* exist and what each one's document is — is model data, computed once by
|
|
424
|
+
* apidef and emitted into Config.
|
|
425
|
+
*
|
|
426
|
+
* Two jobs:
|
|
427
|
+
*
|
|
428
|
+
* graphql_body_util — build { query, variables } for a point, binding the
|
|
429
|
+
* op's arguments to the document's declared variables.
|
|
430
|
+
*
|
|
431
|
+
* graphql_errors_util — lift a GraphQL failure into an SDK error. GraphQL
|
|
432
|
+
* reports failures as a top-level `errors` array under
|
|
433
|
+
* HTTP 200, so the status-driven path in result_basic
|
|
434
|
+
* never sees them. *)
|
|
435
|
+
|
|
436
|
+
(* Content type every GraphQL-over-HTTP request uses. *)
|
|
437
|
+
let graphql_content_type = "application/json"
|
|
438
|
+
|
|
439
|
+
(* Map a GraphQL error to the same error codes the HTTP path produces, so a
|
|
440
|
+
* caller handles auth or rate limiting identically on both transports.
|
|
441
|
+
* Servers put the machine-readable code in `extensions.code`; Linear-style
|
|
442
|
+
* APIs use `extensions.type`. *)
|
|
443
|
+
let graphql_error_code (gqlerr : value) : string =
|
|
444
|
+
let ext = getp gqlerr "extensions" in
|
|
445
|
+
let code = get_str_d ext "code" "" in
|
|
446
|
+
let code = if code = "" then get_str_d ext "type" "" else code in
|
|
447
|
+
let raw = String.uppercase_ascii code in
|
|
448
|
+
if substr_contains raw "AUTH" || substr_contains raw "FORBIDDEN"
|
|
449
|
+
|| substr_contains raw "UNAUTHENTICATED" then "request_auth"
|
|
450
|
+
else if substr_contains raw "RATELIMIT" || substr_contains raw "RATE_LIMIT"
|
|
451
|
+
|| substr_contains raw "TOO_MANY" then "request_ratelimit"
|
|
452
|
+
else if substr_contains raw "BAD_USER_INPUT" || substr_contains raw "VALIDATION"
|
|
453
|
+
|| substr_contains raw "INVALID" then "request_invalid"
|
|
454
|
+
else "request_graphql"
|
|
455
|
+
|
|
456
|
+
(* Build the request body for a GraphQL point.
|
|
457
|
+
*
|
|
458
|
+
* Variables come from the op's own arguments: a named variable binds to the
|
|
459
|
+
* like-named argument (`from`), and the input-object variable (empty `from`)
|
|
460
|
+
* takes the request data as a whole — which is what makes a generated
|
|
461
|
+
* create/update call look exactly like its REST equivalent. *)
|
|
462
|
+
let graphql_body_util (ctx : ctx) : value =
|
|
463
|
+
match getp ctx.c_point "graphql" with
|
|
464
|
+
| Map _ as gql ->
|
|
465
|
+
(* reqmatch/reqdata hold the caller's arguments for THIS call; data/match
|
|
466
|
+
* hold the entity's current state. Which pair depends on whether the op
|
|
467
|
+
* takes match or data input. A named variable falls back to the current
|
|
468
|
+
* state, so updating a loaded entity with just {title} still binds the
|
|
469
|
+
* stored id the mutation requires. *)
|
|
470
|
+
let datainput = ctx.c_op.op_input = "data" in
|
|
471
|
+
let reqsrc = if datainput then ctx.c_reqdata else ctx.c_reqmatch in
|
|
472
|
+
let datasrc = if datainput then ctx.c_data else ctx.c_match in
|
|
473
|
+
let reqsrc = match reqsrc with Map _ as m -> m | _ -> empty_map () in
|
|
474
|
+
let datasrc = match datasrc with Map _ as m -> m | _ -> empty_map () in
|
|
475
|
+
let variables = empty_map () in
|
|
476
|
+
let varlist = match getp gql "vars" with List r -> !r | _ -> [] in
|
|
477
|
+
List.iter (fun spec ->
|
|
478
|
+
match spec with
|
|
479
|
+
| Map _ ->
|
|
480
|
+
let name = get_str_d spec "name" "" in
|
|
481
|
+
let from = get_str_d spec "from" "" in
|
|
482
|
+
if name <> "" then begin
|
|
483
|
+
if from = "" then begin
|
|
484
|
+
(* The input object IS the request body. Strip the action
|
|
485
|
+
* selector, which is an SDK-side point discriminator, not an
|
|
486
|
+
* API field. *)
|
|
487
|
+
let body = empty_map () in
|
|
488
|
+
List.iter (fun k ->
|
|
489
|
+
if k <> "$action" then setp body k (getp reqsrc k))
|
|
490
|
+
(keysof reqsrc);
|
|
491
|
+
setp variables name body
|
|
492
|
+
end
|
|
493
|
+
else
|
|
494
|
+
(* Only send variables the caller actually supplied: sending an
|
|
495
|
+
* explicit null would clear a field on many APIs. *)
|
|
496
|
+
let v = getp reqsrc from in
|
|
497
|
+
let v = if is_nullish v then getp datasrc from else v in
|
|
498
|
+
if not (is_nullish v) then setp variables name v
|
|
499
|
+
end
|
|
500
|
+
| _ -> ())
|
|
501
|
+
varlist;
|
|
502
|
+
jo [("query", getp gql "doc"); ("variables", variables)]
|
|
503
|
+
| _ -> Noval
|
|
504
|
+
|
|
505
|
+
(* Inspect a decoded GraphQL response body and record a failure when the
|
|
506
|
+
* server reported one. Returns true when an error was recorded.
|
|
507
|
+
*
|
|
508
|
+
* Partial data (`data` alongside `errors`) is treated as failure: the REST
|
|
509
|
+
* surface has no partial-success concept, and silently returning half an
|
|
510
|
+
* object would be worse than failing. *)
|
|
511
|
+
let graphql_errors_util (ctx : ctx) : bool =
|
|
512
|
+
match ctx.c_result with
|
|
513
|
+
| None -> false
|
|
514
|
+
| Some result ->
|
|
515
|
+
if get_str_d ctx.c_point "kind" "" <> "graphql" then false
|
|
516
|
+
else
|
|
517
|
+
let errors = match getp result.rt_body "errors" with List r -> !r | _ -> [] in
|
|
518
|
+
let count = List.length errors in
|
|
519
|
+
if count = 0 then false
|
|
520
|
+
else begin
|
|
521
|
+
let first = List.hd errors in
|
|
522
|
+
let msg = get_str_d first "message" "" in
|
|
523
|
+
let msg = if msg = "" then "graphql error" else msg in
|
|
524
|
+
let msg =
|
|
525
|
+
if 1 < count then msg ^ " (+" ^ string_of_int (count - 1) ^ " more)"
|
|
526
|
+
else msg in
|
|
527
|
+
result.rt_err <-
|
|
528
|
+
Some (ctx_make_error ctx (graphql_error_code first) ("graphql: " ^ msg));
|
|
529
|
+
result.rt_ok <- false;
|
|
530
|
+
true
|
|
531
|
+
end
|
|
532
|
+
|
|
419
533
|
let prepare_auth_util (ctx : ctx) : (spec option * sdk_error option) =
|
|
420
534
|
match ctx.c_spec with
|
|
421
535
|
| None -> (None, Some (ctx_make_error ctx "auth_no_spec" "Expected context spec property to be defined."))
|
|
@@ -584,8 +698,23 @@ let make_spec_util (ctx : ctx) : (spec option * sdk_error option) =
|
|
|
584
698
|
sp.sp_params <- u.u_prepare_params ctx;
|
|
585
699
|
sp.sp_query <- u.u_prepare_query ctx;
|
|
586
700
|
sp.sp_headers <- u.u_prepare_headers ctx;
|
|
587
|
-
|
|
588
|
-
|
|
701
|
+
if get_str_d ctx.c_point "kind" "" = "graphql" then begin
|
|
702
|
+
(* GraphQL addresses one endpoint: no path parts, no query string, and
|
|
703
|
+
* the body carries the operation. prepare_body is skipped
|
|
704
|
+
* deliberately — it only emits a body for data-input ops, whereas
|
|
705
|
+
* every GraphQL op posts one, including load/list/remove. *)
|
|
706
|
+
sp.sp_body <- u.u_graphql_body ctx;
|
|
707
|
+
sp.sp_path <- "";
|
|
708
|
+
(* prepare_query already copied the op's match arguments into the query
|
|
709
|
+
* string. Those same values are bound as operation variables, so
|
|
710
|
+
* leaving them would send /graphql?id=i1. *)
|
|
711
|
+
sp.sp_query <- empty_map ();
|
|
712
|
+
setp sp.sp_headers "content-type" (Str graphql_content_type)
|
|
713
|
+
end
|
|
714
|
+
else begin
|
|
715
|
+
sp.sp_body <- u.u_prepare_body ctx;
|
|
716
|
+
sp.sp_path <- u.u_prepare_path ctx
|
|
717
|
+
end;
|
|
589
718
|
(match ctx.c_ctrl.ctrl_explain with Map _ -> setp ctx.c_ctrl.ctrl_explain "spec" (spec_to_value sp) | _ -> ());
|
|
590
719
|
match u.u_prepare_auth ctx with
|
|
591
720
|
| (_, Some err) -> (None, Some err)
|
|
@@ -687,7 +816,13 @@ let make_response_util (ctx : ctx) : (response option * sdk_error option) =
|
|
|
687
816
|
| None -> (None, Some (ctx_make_error ctx "response_no_result" "Expected context result property to be defined."))
|
|
688
817
|
| Some result ->
|
|
689
818
|
spec.sp_step <- "response";
|
|
690
|
-
u.u_result_basic ctx; u.u_result_headers ctx; u.u_result_body ctx;
|
|
819
|
+
u.u_result_basic ctx; u.u_result_headers ctx; u.u_result_body ctx;
|
|
820
|
+
(* GraphQL reports failures as a top-level `errors` array under
|
|
821
|
+
* HTTP 200, so result_basic's status check never sees them. Lift
|
|
822
|
+
* them here, before the response transform tries to unwrap data
|
|
823
|
+
* that is not there. *)
|
|
824
|
+
ignore (u.u_graphql_errors ctx);
|
|
825
|
+
ignore (u.u_transform_response ctx);
|
|
691
826
|
if result.rt_err = None then result.rt_ok <- true;
|
|
692
827
|
(match ctx.c_ctrl.ctrl_explain with Map _ -> setp ctx.c_ctrl.ctrl_explain "result" (result_to_value result) | _ -> ());
|
|
693
828
|
(Some response, None))))
|
|
@@ -763,7 +898,7 @@ let opt_spec_value () : value =
|
|
|
763
898
|
("auth", jo [("prefix", Str "")]);
|
|
764
899
|
("headers", jo [("`$CHILD`", Str "`$STRING`")]);
|
|
765
900
|
("allow", jo [("method", Str "GET,PUT,POST,PATCH,DELETE,OPTIONS");
|
|
766
|
-
("op", Str "create,update,load,list,remove,command,direct")]);
|
|
901
|
+
("op", Str "create,update,load,list,remove,command,direct,graphql")]);
|
|
767
902
|
("entity", jo [("`$CHILD`", jo [("`$OPEN`", Bool true); ("active", Bool false); ("alias", empty_map ())])]);
|
|
768
903
|
("feature", jo [("`$CHILD`", jo [("`$OPEN`", Bool true); ("active", Bool false)])]);
|
|
769
904
|
("utility", empty_map ());
|
|
@@ -898,6 +1033,8 @@ let new_utility () : utility =
|
|
|
898
1033
|
u_prepare_params = prepare_params_util;
|
|
899
1034
|
u_prepare_path = prepare_path_util;
|
|
900
1035
|
u_prepare_query = prepare_query_util;
|
|
1036
|
+
u_graphql_body = graphql_body_util;
|
|
1037
|
+
u_graphql_errors = graphql_errors_util;
|
|
901
1038
|
u_result_basic = result_basic_util;
|
|
902
1039
|
u_result_body = result_body_util;
|
|
903
1040
|
u_result_headers = result_headers_util;
|
|
@@ -932,6 +1069,8 @@ let register (u : utility) : unit =
|
|
|
932
1069
|
u.u_prepare_params <- prepare_params_util;
|
|
933
1070
|
u.u_prepare_path <- prepare_path_util;
|
|
934
1071
|
u.u_prepare_query <- prepare_query_util;
|
|
1072
|
+
u.u_graphql_body <- graphql_body_util;
|
|
1073
|
+
u.u_graphql_errors <- graphql_errors_util;
|
|
935
1074
|
u.u_result_basic <- result_basic_util;
|
|
936
1075
|
u.u_result_body <- result_body_util;
|
|
937
1076
|
u.u_result_headers <- result_headers_util;
|
|
@@ -152,6 +152,8 @@ and utility = {
|
|
|
152
152
|
mutable u_prepare_params : ctx -> value;
|
|
153
153
|
mutable u_prepare_path : ctx -> string;
|
|
154
154
|
mutable u_prepare_query : ctx -> value;
|
|
155
|
+
mutable u_graphql_body : ctx -> value;
|
|
156
|
+
mutable u_graphql_errors : ctx -> bool;
|
|
155
157
|
mutable u_result_basic : ctx -> unit;
|
|
156
158
|
mutable u_result_body : ctx -> unit;
|
|
157
159
|
mutable u_result_headers : ctx -> unit;
|
|
@@ -64,6 +64,14 @@ sub PreRequest {
|
|
|
64
64
|
$paging = $ctx->{ctrl}{paging};
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
# GraphQL paginates through operation VARIABLES, not the query string.
|
|
68
|
+
# This hook runs after make_spec, so spec->{body} already holds the
|
|
69
|
+
# { query, variables } envelope, and before make_fetch_def serialises it.
|
|
70
|
+
if ('graphql' eq (ProjectNameHelpers::gp($ctx->{point}, 'kind') // '')) {
|
|
71
|
+
$self->_graphql_pre_request($ctx, $paging);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
67
75
|
if (defined $paging->{cursor}) {
|
|
68
76
|
$spec->{query}{$cursor_param} = $paging->{cursor};
|
|
69
77
|
}
|
|
@@ -79,6 +87,45 @@ sub PreRequest {
|
|
|
79
87
|
return;
|
|
80
88
|
}
|
|
81
89
|
|
|
90
|
+
# Relay pagination: the cursor is the `after` variable (or whatever the model
|
|
91
|
+
# named it), and the page size is `first`.
|
|
92
|
+
sub _graphql_pre_request {
|
|
93
|
+
my ($self, $ctx, $paging) = @_;
|
|
94
|
+
|
|
95
|
+
my $body = $ctx->{spec}{body};
|
|
96
|
+
return unless ref($body) eq 'HASH';
|
|
97
|
+
|
|
98
|
+
my $variables = $body->{variables};
|
|
99
|
+
unless (ref($variables) eq 'HASH') {
|
|
100
|
+
$variables = {};
|
|
101
|
+
$body->{variables} = $variables;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
my $after_var = defined $self->{options}{afterVar} ? $self->{options}{afterVar} : 'after';
|
|
105
|
+
my $first_var = defined $self->{options}{firstVar} ? $self->{options}{firstVar} : 'first';
|
|
106
|
+
|
|
107
|
+
# Only bind variables the operation actually declares, or the server
|
|
108
|
+
# rejects the document.
|
|
109
|
+
my %declared;
|
|
110
|
+
my $varlist = ProjectNameHelpers::gpath($ctx->{point}, 'graphql.vars');
|
|
111
|
+
if (ref($varlist) eq 'ARRAY') {
|
|
112
|
+
for my $v (@$varlist) {
|
|
113
|
+
my $name = ProjectNameHelpers::gp($v, 'name');
|
|
114
|
+
$declared{$name} = 1 if defined $name;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (defined $paging->{cursor} && $declared{$after_var}) {
|
|
119
|
+
$variables->{$after_var} = $paging->{cursor};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (defined $self->{options}{limit} && !defined $variables->{$first_var}
|
|
123
|
+
&& $declared{$first_var}) {
|
|
124
|
+
$variables->{$first_var} = $self->{options}{limit};
|
|
125
|
+
}
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
82
129
|
sub PreResult {
|
|
83
130
|
my ($self, $ctx) = @_;
|
|
84
131
|
return unless $self->{active};
|
|
@@ -106,6 +153,39 @@ sub PreResult {
|
|
|
106
153
|
}
|
|
107
154
|
}
|
|
108
155
|
|
|
156
|
+
# Set when the response states hasMore outright, rather than leaving it to
|
|
157
|
+
# be inferred from the presence of a cursor.
|
|
158
|
+
my $explicit_more = 0;
|
|
159
|
+
|
|
160
|
+
# Relay connections carry the cursor in pageInfo, at the path the model
|
|
161
|
+
# recorded for this op.
|
|
162
|
+
my $page = ProjectNameHelpers::gpath($ctx->{point}, 'graphql.page');
|
|
163
|
+
if (ref($page) eq 'HASH' && Voxgig::Struct::ismap($body)) {
|
|
164
|
+
# `connpath` locates the connection object inside the response envelope
|
|
165
|
+
# (data.<field>); the cursor/more paths are relative to it.
|
|
166
|
+
my $conn = $body;
|
|
167
|
+
my $connpath = $page->{connpath};
|
|
168
|
+
if (defined $connpath && '' ne $connpath) {
|
|
169
|
+
my $sub = ProjectNameHelpers::gpath($body, $connpath);
|
|
170
|
+
$conn = $sub if defined $sub;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
my $cursorpath = $page->{cursor};
|
|
174
|
+
if (defined $cursorpath && '' ne $cursorpath) {
|
|
175
|
+
my $cursor = ProjectNameHelpers::gpath($conn, $cursorpath);
|
|
176
|
+
$paging->{cursor} = $cursor if defined $cursor;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
my $morepath = $page->{more};
|
|
180
|
+
if (defined $morepath && '' ne $morepath) {
|
|
181
|
+
my $more = ProjectNameHelpers::gpath($conn, $morepath);
|
|
182
|
+
if (ProjectNameHelpers::is_true($more) || ProjectNameHelpers::is_false($more)) {
|
|
183
|
+
$paging->{hasMore} = ProjectNameHelpers::is_true($more) ? 1 : 0;
|
|
184
|
+
$explicit_more = 1;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
109
189
|
# Body-level cursors.
|
|
110
190
|
if (Voxgig::Struct::ismap($body)) {
|
|
111
191
|
my $bnext = ProjectNameHelpers::gp($body, 'next');
|
|
@@ -118,12 +198,20 @@ sub PreResult {
|
|
|
118
198
|
my $bhasmore = $body->{hasMore};
|
|
119
199
|
if (ProjectNameHelpers::is_true($bhasmore) || ProjectNameHelpers::is_false($bhasmore)) {
|
|
120
200
|
$paging->{hasMore} = ProjectNameHelpers::is_true($bhasmore) ? 1 : 0;
|
|
201
|
+
$explicit_more = 1;
|
|
121
202
|
}
|
|
122
203
|
}
|
|
123
204
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
205
|
+
# Cursor presence only INFERS another page. When the server stated the
|
|
206
|
+
# answer outright - relay's `hasNextPage: false`, or a body `hasMore` -
|
|
207
|
+
# that wins: a final page normally carries both an end cursor and
|
|
208
|
+
# hasNextPage false, and inferring from the cursor there would send the
|
|
209
|
+
# caller back for a page that does not exist, forever.
|
|
210
|
+
unless ($explicit_more) {
|
|
211
|
+
$paging->{hasMore} = ($paging->{hasMore}
|
|
212
|
+
|| defined $paging->{next} || defined $paging->{cursor}
|
|
213
|
+
|| defined $paging->{nextPage}) ? 1 : 0;
|
|
214
|
+
}
|
|
127
215
|
|
|
128
216
|
$result->{paging} = $paging;
|
|
129
217
|
|