@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
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# ProjectName SDK utility: graphql
|
|
2
|
+
#
|
|
3
|
+
# GraphQL transport. API-INDEPENDENT: every GraphQL SDK this generator
|
|
4
|
+
# produces uses this file unchanged. The API-specific part — which
|
|
5
|
+
# operations exist and what each one's document is — is model data,
|
|
6
|
+
# computed once by apidef and emitted into Config.
|
|
7
|
+
#
|
|
8
|
+
# Two jobs:
|
|
9
|
+
#
|
|
10
|
+
# graphql_body — build { query, variables } for a point, binding the
|
|
11
|
+
# op's arguments to the document's declared variables.
|
|
12
|
+
#
|
|
13
|
+
# graphql_errors — lift a GraphQL failure into an SDK error. GraphQL
|
|
14
|
+
# reports failures as a top-level `errors` array under
|
|
15
|
+
# HTTP 200, so the status-driven path in result_basic
|
|
16
|
+
# never sees them.
|
|
17
|
+
|
|
18
|
+
use strict;
|
|
19
|
+
use warnings;
|
|
20
|
+
|
|
21
|
+
use File::Basename ();
|
|
22
|
+
use Cwd ();
|
|
23
|
+
|
|
24
|
+
my $__dir;
|
|
25
|
+
BEGIN { $__dir = File::Basename::dirname(Cwd::abs_path(__FILE__)) }
|
|
26
|
+
require(Cwd::abs_path("$__dir/../lib/Voxgig/Struct.pm"));
|
|
27
|
+
require(Cwd::abs_path("$__dir/../core/helpers.pm"));
|
|
28
|
+
|
|
29
|
+
package ProjectNameUtilities;
|
|
30
|
+
|
|
31
|
+
our %REGISTRY;
|
|
32
|
+
|
|
33
|
+
# Content type every GraphQL-over-HTTP request uses.
|
|
34
|
+
our $GRAPHQL_CONTENT_TYPE = 'application/json';
|
|
35
|
+
|
|
36
|
+
# Map a GraphQL error to the same error codes the HTTP path produces, so a
|
|
37
|
+
# caller handles auth or rate limiting identically on both transports.
|
|
38
|
+
# Servers put the machine-readable code in `extensions.code`; Linear-style
|
|
39
|
+
# APIs use `extensions.type`.
|
|
40
|
+
sub graphql_error_code {
|
|
41
|
+
my ($gqlerr) = @_;
|
|
42
|
+
|
|
43
|
+
my $ext = ProjectNameHelpers::gp($gqlerr, 'extensions');
|
|
44
|
+
|
|
45
|
+
my $raw = ProjectNameHelpers::gp($ext, 'code');
|
|
46
|
+
$raw = ProjectNameHelpers::gp($ext, 'type') if !defined $raw || '' eq $raw;
|
|
47
|
+
$raw = defined $raw ? uc("$raw") : '';
|
|
48
|
+
|
|
49
|
+
return 'request_auth'
|
|
50
|
+
if $raw =~ /AUTH/ || $raw =~ /FORBIDDEN/ || $raw =~ /UNAUTHENTICATED/;
|
|
51
|
+
return 'request_ratelimit'
|
|
52
|
+
if $raw =~ /RATELIMIT/ || $raw =~ /RATE_LIMIT/ || $raw =~ /TOO_MANY/;
|
|
53
|
+
return 'request_invalid'
|
|
54
|
+
if $raw =~ /BAD_USER_INPUT/ || $raw =~ /VALIDATION/ || $raw =~ /INVALID/;
|
|
55
|
+
|
|
56
|
+
return 'request_graphql';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Build the request body for a GraphQL point.
|
|
60
|
+
#
|
|
61
|
+
# Variables come from the op's own arguments: a named variable binds to the
|
|
62
|
+
# like-named argument (`from`), and the input-object variable (empty
|
|
63
|
+
# `from`) takes the request data as a whole — which is what makes a
|
|
64
|
+
# generated create/update call look exactly like its REST equivalent.
|
|
65
|
+
$REGISTRY{graphql_body} = sub {
|
|
66
|
+
my ($ctx) = @_;
|
|
67
|
+
|
|
68
|
+
my $gql = ProjectNameHelpers::gp($ctx->{point}, 'graphql');
|
|
69
|
+
return undef unless ref($gql) eq 'HASH';
|
|
70
|
+
|
|
71
|
+
# reqmatch/reqdata hold the caller's arguments for THIS call; data/match
|
|
72
|
+
# hold the entity's current state. Which pair depends on whether the op
|
|
73
|
+
# takes match or data input. A named variable falls back to the current
|
|
74
|
+
# state, so updating a loaded entity with just {title} still binds the
|
|
75
|
+
# stored id the mutation requires.
|
|
76
|
+
my $datainput =
|
|
77
|
+
$ctx->{op} && defined $ctx->{op}{input} && 'data' eq $ctx->{op}{input};
|
|
78
|
+
my $reqsrc = $datainput ? $ctx->{reqdata} : $ctx->{reqmatch};
|
|
79
|
+
my $datasrc = $datainput ? $ctx->{data} : $ctx->{match};
|
|
80
|
+
$reqsrc = {} unless ref($reqsrc) eq 'HASH';
|
|
81
|
+
$datasrc = {} unless ref($datasrc) eq 'HASH';
|
|
82
|
+
|
|
83
|
+
my %variables;
|
|
84
|
+
|
|
85
|
+
my $varlist = ProjectNameHelpers::gp($gql, 'vars');
|
|
86
|
+
$varlist = [] unless ref($varlist) eq 'ARRAY';
|
|
87
|
+
|
|
88
|
+
for my $spec (@$varlist) {
|
|
89
|
+
next unless ref($spec) eq 'HASH';
|
|
90
|
+
|
|
91
|
+
my $name = ProjectNameHelpers::gp($spec, 'name');
|
|
92
|
+
my $from = ProjectNameHelpers::gp($spec, 'from');
|
|
93
|
+
next unless defined $name;
|
|
94
|
+
|
|
95
|
+
if (!defined $from || '' eq $from) {
|
|
96
|
+
# The input object IS the request body. Strip the action selector,
|
|
97
|
+
# which is an SDK-side point discriminator, not an API field.
|
|
98
|
+
my %body;
|
|
99
|
+
for my $k (keys %$reqsrc) {
|
|
100
|
+
$body{$k} = $reqsrc->{$k} unless '$action' eq $k;
|
|
101
|
+
}
|
|
102
|
+
$variables{$name} = \%body;
|
|
103
|
+
next;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# Only send variables the caller actually supplied: sending an explicit
|
|
107
|
+
# null would clear a field on many APIs.
|
|
108
|
+
my $val = ProjectNameHelpers::gp($reqsrc, $from);
|
|
109
|
+
$val = ProjectNameHelpers::gp($datasrc, $from) unless defined $val;
|
|
110
|
+
$variables{$name} = $val if defined $val;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
query => ProjectNameHelpers::gp($gql, 'doc'),
|
|
115
|
+
variables => \%variables,
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
# Inspect a decoded GraphQL response body and record a failure when the
|
|
120
|
+
# server reported one. Returns true when an error was recorded.
|
|
121
|
+
#
|
|
122
|
+
# Partial data (`data` alongside `errors`) is treated as failure: the REST
|
|
123
|
+
# surface has no partial-success concept, and silently returning half an
|
|
124
|
+
# object would be worse than failing.
|
|
125
|
+
$REGISTRY{graphql_errors} = sub {
|
|
126
|
+
my ($ctx) = @_;
|
|
127
|
+
|
|
128
|
+
my $result = $ctx->{result};
|
|
129
|
+
return 0 unless $result && $ctx->{point};
|
|
130
|
+
|
|
131
|
+
my $kind = ProjectNameHelpers::gp($ctx->{point}, 'kind');
|
|
132
|
+
return 0 unless defined $kind && 'graphql' eq $kind;
|
|
133
|
+
|
|
134
|
+
my $errors = ProjectNameHelpers::gp($result->{body}, 'errors');
|
|
135
|
+
return 0 unless ref($errors) eq 'ARRAY' && 0 < scalar(@$errors);
|
|
136
|
+
|
|
137
|
+
my $first = $errors->[0];
|
|
138
|
+
my $msg = ProjectNameHelpers::gp($first, 'message');
|
|
139
|
+
$msg = 'graphql error' unless defined $msg && '' ne $msg;
|
|
140
|
+
$msg = $msg . ' (+' . (scalar(@$errors) - 1) . ' more)'
|
|
141
|
+
if 1 < scalar(@$errors);
|
|
142
|
+
|
|
143
|
+
$result->{err} =
|
|
144
|
+
$ctx->make_error(graphql_error_code($first), 'graphql: ' . $msg);
|
|
145
|
+
$result->{ok} = 0;
|
|
146
|
+
|
|
147
|
+
return 1;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
1;
|
|
@@ -70,7 +70,7 @@ $REGISTRY{make_options} = sub {
|
|
|
70
70
|
'headers' => { '`$CHILD`' => '`$STRING`' },
|
|
71
71
|
'allow' => {
|
|
72
72
|
'method' => 'GET,PUT,POST,PATCH,DELETE,OPTIONS',
|
|
73
|
-
'op' => 'create,update,load,list,remove,command,direct',
|
|
73
|
+
'op' => 'create,update,load,list,remove,command,direct,graphql',
|
|
74
74
|
},
|
|
75
75
|
'entity' => { '`$CHILD`' => { '`$OPEN`' => $JT, 'active' => $JF, 'alias' => {} } },
|
|
76
76
|
'feature' => { '`$CHILD`' => { '`$OPEN`' => $JT, 'active' => $JF } },
|
|
@@ -27,6 +27,12 @@ $REGISTRY{make_response} = sub {
|
|
|
27
27
|
$utility->{result_basic}->($ctx);
|
|
28
28
|
$utility->{result_headers}->($ctx);
|
|
29
29
|
$utility->{result_body}->($ctx);
|
|
30
|
+
|
|
31
|
+
# GraphQL reports failures as a top-level `errors` array under HTTP 200,
|
|
32
|
+
# so result_basic's status check never sees them. Lift them here, before
|
|
33
|
+
# the response transform tries to unwrap data that is not there.
|
|
34
|
+
$utility->{graphql_errors}->($ctx);
|
|
35
|
+
|
|
30
36
|
$utility->{transform_response}->($ctx);
|
|
31
37
|
|
|
32
38
|
$result->{ok} = 1 if !defined $result->{err};
|
|
@@ -56,8 +56,26 @@ $REGISTRY{make_spec} = sub {
|
|
|
56
56
|
$ctx->{spec}{params} = $utility->{prepare_params}->($ctx);
|
|
57
57
|
$ctx->{spec}{query} = $utility->{prepare_query}->($ctx);
|
|
58
58
|
$ctx->{spec}{headers} = $utility->{prepare_headers}->($ctx);
|
|
59
|
-
|
|
60
|
-
$
|
|
59
|
+
|
|
60
|
+
my $pkind = ProjectNameHelpers::gp($ctx->{point}, 'kind');
|
|
61
|
+
if (defined $pkind && 'graphql' eq $pkind) {
|
|
62
|
+
# GraphQL addresses one endpoint: no path parts, no query string, and
|
|
63
|
+
# the body carries the operation. prepare_body is skipped deliberately
|
|
64
|
+
# — it only emits a body for data-input ops, whereas every GraphQL op
|
|
65
|
+
# posts one, including load/list/remove.
|
|
66
|
+
$ctx->{spec}{body} = $utility->{graphql_body}->($ctx);
|
|
67
|
+
$ctx->{spec}{path} = '';
|
|
68
|
+
# prepare_query already copied the op's match arguments into the query
|
|
69
|
+
# string. Those same values are bound as operation variables, so
|
|
70
|
+
# leaving them would send /graphql?id=i1.
|
|
71
|
+
$ctx->{spec}{query} = {};
|
|
72
|
+
$ctx->{spec}{headers}{'content-type'} =
|
|
73
|
+
$ProjectNameUtilities::GRAPHQL_CONTENT_TYPE;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
$ctx->{spec}{body} = $utility->{prepare_body}->($ctx);
|
|
77
|
+
$ctx->{spec}{path} = $utility->{prepare_path}->($ctx);
|
|
78
|
+
}
|
|
61
79
|
|
|
62
80
|
$ctx->{ctrl}{explain}{spec} = $ctx->{spec} if $ctx->{ctrl}{explain};
|
|
63
81
|
|
|
@@ -35,6 +35,7 @@ require(Cwd::abs_path("$__dir/prepare_method.pm"));
|
|
|
35
35
|
require(Cwd::abs_path("$__dir/prepare_params.pm"));
|
|
36
36
|
require(Cwd::abs_path("$__dir/prepare_path.pm"));
|
|
37
37
|
require(Cwd::abs_path("$__dir/prepare_query.pm"));
|
|
38
|
+
require(Cwd::abs_path("$__dir/graphql.pm"));
|
|
38
39
|
require(Cwd::abs_path("$__dir/result_basic.pm"));
|
|
39
40
|
require(Cwd::abs_path("$__dir/result_body.pm"));
|
|
40
41
|
require(Cwd::abs_path("$__dir/result_headers.pm"));
|
|
@@ -29,6 +29,8 @@ class ProjectNameUtility
|
|
|
29
29
|
public mixed $prepare_params = null;
|
|
30
30
|
public mixed $prepare_path = null;
|
|
31
31
|
public mixed $prepare_query = null;
|
|
32
|
+
public mixed $graphql_body = null;
|
|
33
|
+
public mixed $graphql_errors = null;
|
|
32
34
|
public mixed $result_basic = null;
|
|
33
35
|
public mixed $result_body = null;
|
|
34
36
|
public mixed $result_headers = null;
|
|
@@ -57,6 +57,15 @@ class ProjectNamePagingFeature extends ProjectNameBaseFeature
|
|
|
57
57
|
$paging = [];
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// GraphQL paginates through operation VARIABLES, not the query
|
|
61
|
+
// string. This hook runs after makeSpec, so spec->body already holds
|
|
62
|
+
// the { query, variables } envelope, and before makeFetchDef
|
|
63
|
+
// serialises it.
|
|
64
|
+
if ('graphql' === \Voxgig\Struct\Struct::getprop($ctx->point, 'kind')) {
|
|
65
|
+
$this->_graphql_pre_request($ctx, $paging);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
60
69
|
if (($paging['cursor'] ?? null) !== null) {
|
|
61
70
|
$spec->query[$cursor_param] = $paging['cursor'];
|
|
62
71
|
} elseif (($spec->query[$page_param] ?? null) === null) {
|
|
@@ -72,6 +81,53 @@ class ProjectNamePagingFeature extends ProjectNameBaseFeature
|
|
|
72
81
|
}
|
|
73
82
|
}
|
|
74
83
|
|
|
84
|
+
// Relay pagination: the cursor is the `after` variable (or whatever the
|
|
85
|
+
// model named it), and the page size is `first`.
|
|
86
|
+
private function _graphql_pre_request(ProjectNameContext $ctx, array $paging): void
|
|
87
|
+
{
|
|
88
|
+
$body = $ctx->spec->body;
|
|
89
|
+
if (!is_array($body)) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
$variables = $body['variables'] ?? null;
|
|
94
|
+
if (!is_array($variables)) {
|
|
95
|
+
$variables = [];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
$after_var = $this->options['afterVar'] ?? 'after';
|
|
99
|
+
$first_var = $this->options['firstVar'] ?? 'first';
|
|
100
|
+
|
|
101
|
+
// Only bind variables the operation actually declares, or the server
|
|
102
|
+
// rejects the document.
|
|
103
|
+
$declared = [];
|
|
104
|
+
$varlist = \Voxgig\Struct\Struct::getpath($ctx->point, 'graphql.vars');
|
|
105
|
+
if (is_array($varlist)) {
|
|
106
|
+
foreach ($varlist as $v) {
|
|
107
|
+
$name = \Voxgig\Struct\Struct::getprop($v, 'name');
|
|
108
|
+
if (null !== $name) {
|
|
109
|
+
$declared[$name] = true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (($paging['cursor'] ?? null) !== null
|
|
115
|
+
&& ($declared[$after_var] ?? false)) {
|
|
116
|
+
$variables[$after_var] = $paging['cursor'];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (($this->options['limit'] ?? null) !== null
|
|
120
|
+
&& ($variables[$first_var] ?? null) === null
|
|
121
|
+
&& ($declared[$first_var] ?? false)) {
|
|
122
|
+
$variables[$first_var] = $this->options['limit'];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// PHP arrays are values, not references: write the whole envelope
|
|
126
|
+
// back or the bindings are lost.
|
|
127
|
+
$body['variables'] = $variables;
|
|
128
|
+
$ctx->spec->body = $body;
|
|
129
|
+
}
|
|
130
|
+
|
|
75
131
|
public function PreResult(ProjectNameContext $ctx): void
|
|
76
132
|
{
|
|
77
133
|
if (!$this->active || !$this->_is_list($ctx)) {
|
|
@@ -100,6 +156,44 @@ class ProjectNamePagingFeature extends ProjectNameBaseFeature
|
|
|
100
156
|
$paging['next'] = $m[1];
|
|
101
157
|
}
|
|
102
158
|
|
|
159
|
+
// Set when the response states hasMore outright, rather than leaving
|
|
160
|
+
// it to be inferred from the presence of a cursor.
|
|
161
|
+
$explicit_more = false;
|
|
162
|
+
|
|
163
|
+
// Relay connections carry the cursor in pageInfo, at the path the
|
|
164
|
+
// model recorded for this op.
|
|
165
|
+
$page = \Voxgig\Struct\Struct::getpath($ctx->point, 'graphql.page');
|
|
166
|
+
if (is_array($page) && is_array($body)) {
|
|
167
|
+
// `connpath` locates the connection object inside the response
|
|
168
|
+
// envelope (data.<field>); the cursor/more paths are relative
|
|
169
|
+
// to it.
|
|
170
|
+
$connpath = $page['connpath'] ?? null;
|
|
171
|
+
$conn = $body;
|
|
172
|
+
if (is_string($connpath) && '' !== $connpath) {
|
|
173
|
+
$sub = \Voxgig\Struct\Struct::getpath($body, $connpath);
|
|
174
|
+
if (null !== $sub) {
|
|
175
|
+
$conn = $sub;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
$cursorpath = $page['cursor'] ?? null;
|
|
180
|
+
if (is_string($cursorpath) && '' !== $cursorpath) {
|
|
181
|
+
$cursor = \Voxgig\Struct\Struct::getpath($conn, $cursorpath);
|
|
182
|
+
if (null !== $cursor) {
|
|
183
|
+
$paging['cursor'] = $cursor;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
$morepath = $page['more'] ?? null;
|
|
188
|
+
if (is_string($morepath) && '' !== $morepath) {
|
|
189
|
+
$more = \Voxgig\Struct\Struct::getpath($conn, $morepath);
|
|
190
|
+
if (is_bool($more)) {
|
|
191
|
+
$paging['hasMore'] = $more;
|
|
192
|
+
$explicit_more = true;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
103
197
|
// Body-level cursors.
|
|
104
198
|
if (is_array($body)) {
|
|
105
199
|
if (($body['next'] ?? null) !== null) {
|
|
@@ -113,13 +207,21 @@ class ProjectNamePagingFeature extends ProjectNameBaseFeature
|
|
|
113
207
|
}
|
|
114
208
|
if (is_bool($body['hasMore'] ?? null)) {
|
|
115
209
|
$paging['hasMore'] = $body['hasMore'];
|
|
210
|
+
$explicit_more = true;
|
|
116
211
|
}
|
|
117
212
|
}
|
|
118
213
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
214
|
+
// Cursor presence only INFERS another page. When the server stated
|
|
215
|
+
// the answer outright — relay's `hasNextPage: false`, or a body
|
|
216
|
+
// `hasMore` — that wins: a final page normally carries both an end
|
|
217
|
+
// cursor and hasNextPage false, and inferring from the cursor there
|
|
218
|
+
// would send the caller back for a page that does not exist, forever.
|
|
219
|
+
if (!$explicit_more) {
|
|
220
|
+
$paging['hasMore'] = $paging['hasMore']
|
|
221
|
+
|| $paging['next'] !== null
|
|
222
|
+
|| $paging['cursor'] !== null
|
|
223
|
+
|| $paging['nextPage'] !== null;
|
|
224
|
+
}
|
|
123
225
|
|
|
124
226
|
$result->paging = $paging;
|
|
125
227
|
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
declare(strict_types=1);
|
|
3
|
+
|
|
4
|
+
// ProjectName SDK utility: graphql
|
|
5
|
+
//
|
|
6
|
+
// GraphQL transport. API-INDEPENDENT: every GraphQL SDK this generator
|
|
7
|
+
// produces uses this file unchanged. The API-specific part — which
|
|
8
|
+
// operations exist and what each one's document is — is model data,
|
|
9
|
+
// computed once by apidef and emitted into Config.
|
|
10
|
+
//
|
|
11
|
+
// Two jobs:
|
|
12
|
+
//
|
|
13
|
+
// graphql_body — build { query, variables } for a point, binding the
|
|
14
|
+
// op's arguments to the document's declared variables.
|
|
15
|
+
//
|
|
16
|
+
// graphql_errors — lift a GraphQL failure into an SDK error. GraphQL
|
|
17
|
+
// reports failures as a top-level `errors` array under
|
|
18
|
+
// HTTP 200, so the status-driven path in result_basic
|
|
19
|
+
// never sees them.
|
|
20
|
+
|
|
21
|
+
class ProjectNameGraphql
|
|
22
|
+
{
|
|
23
|
+
// Content type every GraphQL-over-HTTP request uses.
|
|
24
|
+
public const CONTENT_TYPE = 'application/json';
|
|
25
|
+
|
|
26
|
+
// Map a GraphQL error to the same error codes the HTTP path produces,
|
|
27
|
+
// so a caller handles auth or rate limiting identically on both
|
|
28
|
+
// transports. Servers put the machine-readable code in
|
|
29
|
+
// `extensions.code`; Linear-style APIs use `extensions.type`.
|
|
30
|
+
public static function errorCode(mixed $gqlerr): string
|
|
31
|
+
{
|
|
32
|
+
$ext = \Voxgig\Struct\Struct::getprop($gqlerr, 'extensions');
|
|
33
|
+
if (!is_array($ext)) {
|
|
34
|
+
$ext = [];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
$raw = \Voxgig\Struct\Struct::getprop($ext, 'code');
|
|
38
|
+
if (null === $raw || '' === $raw) {
|
|
39
|
+
$raw = \Voxgig\Struct\Struct::getprop($ext, 'type');
|
|
40
|
+
}
|
|
41
|
+
$raw = strtoupper(is_string($raw) ? $raw : '');
|
|
42
|
+
|
|
43
|
+
if (str_contains($raw, 'AUTH') || str_contains($raw, 'FORBIDDEN')
|
|
44
|
+
|| str_contains($raw, 'UNAUTHENTICATED')) {
|
|
45
|
+
return 'request_auth';
|
|
46
|
+
}
|
|
47
|
+
if (str_contains($raw, 'RATELIMIT') || str_contains($raw, 'RATE_LIMIT')
|
|
48
|
+
|| str_contains($raw, 'TOO_MANY')) {
|
|
49
|
+
return 'request_ratelimit';
|
|
50
|
+
}
|
|
51
|
+
if (str_contains($raw, 'BAD_USER_INPUT') || str_contains($raw, 'VALIDATION')
|
|
52
|
+
|| str_contains($raw, 'INVALID')) {
|
|
53
|
+
return 'request_invalid';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return 'request_graphql';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Build the request body for a GraphQL point.
|
|
60
|
+
//
|
|
61
|
+
// Variables come from the op's own arguments: a named variable binds
|
|
62
|
+
// to the like-named argument (`from`), and the input-object variable
|
|
63
|
+
// (empty `from`) takes the request data as a whole — which is what
|
|
64
|
+
// makes a generated create/update call look exactly like its REST
|
|
65
|
+
// equivalent.
|
|
66
|
+
public static function body(ProjectNameContext $ctx): mixed
|
|
67
|
+
{
|
|
68
|
+
$gql = \Voxgig\Struct\Struct::getprop($ctx->point, 'graphql');
|
|
69
|
+
if (!is_array($gql)) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// reqmatch/reqdata hold the caller's arguments for THIS call;
|
|
74
|
+
// data/match hold the entity's current state. Which pair depends on
|
|
75
|
+
// whether the op takes match or data input. A named variable falls
|
|
76
|
+
// back to the current state, so updating a loaded entity with just
|
|
77
|
+
// {title} still binds the stored id the mutation requires.
|
|
78
|
+
$reqsrc = $ctx->reqmatch;
|
|
79
|
+
$datasrc = $ctx->match;
|
|
80
|
+
if (null !== $ctx->op && 'data' === $ctx->op->input) {
|
|
81
|
+
$reqsrc = $ctx->reqdata;
|
|
82
|
+
$datasrc = $ctx->data;
|
|
83
|
+
}
|
|
84
|
+
if (!is_array($reqsrc)) {
|
|
85
|
+
$reqsrc = [];
|
|
86
|
+
}
|
|
87
|
+
if (!is_array($datasrc)) {
|
|
88
|
+
$datasrc = [];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
$variables = [];
|
|
92
|
+
|
|
93
|
+
$varlist = \Voxgig\Struct\Struct::getprop($gql, 'vars');
|
|
94
|
+
if (!is_array($varlist)) {
|
|
95
|
+
$varlist = [];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
foreach ($varlist as $spec) {
|
|
99
|
+
if (!is_array($spec)) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
$name = \Voxgig\Struct\Struct::getprop($spec, 'name');
|
|
104
|
+
$from = \Voxgig\Struct\Struct::getprop($spec, 'from');
|
|
105
|
+
|
|
106
|
+
if (null === $from || '' === $from) {
|
|
107
|
+
// The input object IS the request body. Strip the action
|
|
108
|
+
// selector, which is an SDK-side point discriminator, not
|
|
109
|
+
// an API field.
|
|
110
|
+
$body = [];
|
|
111
|
+
foreach ($reqsrc as $k => $v) {
|
|
112
|
+
if ('$action' !== $k) {
|
|
113
|
+
$body[$k] = $v;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
$variables[$name] = $body;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Only send variables the caller actually supplied: sending an
|
|
121
|
+
// explicit null would clear a field on many APIs.
|
|
122
|
+
$val = \Voxgig\Struct\Struct::getprop($reqsrc, $from);
|
|
123
|
+
if (null === $val) {
|
|
124
|
+
$val = \Voxgig\Struct\Struct::getprop($datasrc, $from);
|
|
125
|
+
}
|
|
126
|
+
if (null !== $val) {
|
|
127
|
+
$variables[$name] = $val;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return [
|
|
132
|
+
'query' => \Voxgig\Struct\Struct::getprop($gql, 'doc'),
|
|
133
|
+
'variables' => $variables,
|
|
134
|
+
];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Inspect a decoded GraphQL response body and record a failure when
|
|
138
|
+
// the server reported one. Returns true when an error was recorded.
|
|
139
|
+
//
|
|
140
|
+
// Partial data (`data` alongside `errors`) is treated as failure: the
|
|
141
|
+
// REST surface has no partial-success concept, and silently returning
|
|
142
|
+
// half an object would be worse than failing. The raw envelope stays
|
|
143
|
+
// available on the result for callers that need it.
|
|
144
|
+
public static function errors(ProjectNameContext $ctx): bool
|
|
145
|
+
{
|
|
146
|
+
$result = $ctx->result;
|
|
147
|
+
$point = $ctx->point;
|
|
148
|
+
|
|
149
|
+
if (null === $result || null === $point) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if ('graphql' !== \Voxgig\Struct\Struct::getprop($point, 'kind')) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
$errors = \Voxgig\Struct\Struct::getprop($result->body, 'errors');
|
|
158
|
+
if (!is_array($errors) || 0 === count($errors)) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
$first = $errors[0];
|
|
163
|
+
$msg = \Voxgig\Struct\Struct::getprop($first, 'message');
|
|
164
|
+
if (!is_string($msg) || '' === $msg) {
|
|
165
|
+
$msg = 'graphql error';
|
|
166
|
+
}
|
|
167
|
+
if (1 < count($errors)) {
|
|
168
|
+
$msg = $msg . ' (+' . (count($errors) - 1) . ' more)';
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
$result->err = $ctx->make_error(self::errorCode($first), 'graphql: ' . $msg);
|
|
172
|
+
$result->ok = false;
|
|
173
|
+
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -80,7 +80,7 @@ class ProjectNameMakeOptions
|
|
|
80
80
|
'headers' => ['`$CHILD`' => '`$STRING`'],
|
|
81
81
|
'allow' => [
|
|
82
82
|
'method' => 'GET,PUT,POST,PATCH,DELETE,OPTIONS',
|
|
83
|
-
'op' => 'create,update,load,list,remove,command,direct',
|
|
83
|
+
'op' => 'create,update,load,list,remove,command,direct,graphql',
|
|
84
84
|
],
|
|
85
85
|
'entity' => ['`$CHILD`' => ['`$OPEN`' => true, 'active' => false, 'alias' => (object)[]]],
|
|
86
86
|
'feature' => ['`$CHILD`' => ['`$OPEN`' => true, 'active' => false]],
|
|
@@ -29,6 +29,13 @@ class ProjectNameMakeResponse
|
|
|
29
29
|
($utility->result_basic)($ctx);
|
|
30
30
|
($utility->result_headers)($ctx);
|
|
31
31
|
($utility->result_body)($ctx);
|
|
32
|
+
|
|
33
|
+
// GraphQL reports failures as a top-level `errors` array under
|
|
34
|
+
// HTTP 200, so result_basic's status check never sees them. Lift
|
|
35
|
+
// them here, before the response transform tries to unwrap data
|
|
36
|
+
// that is not there.
|
|
37
|
+
($utility->graphql_errors)($ctx);
|
|
38
|
+
|
|
32
39
|
($utility->transform_response)($ctx);
|
|
33
40
|
|
|
34
41
|
if ($result->err === null) {
|
|
@@ -3,6 +3,8 @@ declare(strict_types=1);
|
|
|
3
3
|
|
|
4
4
|
// ProjectName SDK utility: make_spec
|
|
5
5
|
|
|
6
|
+
require_once __DIR__ . '/Graphql.php';
|
|
7
|
+
|
|
6
8
|
require_once __DIR__ . '/../core/Spec.php';
|
|
7
9
|
|
|
8
10
|
class ProjectNameMakeSpec
|
|
@@ -46,8 +48,24 @@ class ProjectNameMakeSpec
|
|
|
46
48
|
$ctx->spec->params = ($utility->prepare_params)($ctx);
|
|
47
49
|
$ctx->spec->query = ($utility->prepare_query)($ctx);
|
|
48
50
|
$ctx->spec->headers = ($utility->prepare_headers)($ctx);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
|
|
52
|
+
if ('graphql' === \Voxgig\Struct\Struct::getprop($point, 'kind')) {
|
|
53
|
+
// GraphQL addresses one endpoint: no path parts, no query
|
|
54
|
+
// string, and the body carries the operation. prepare_body is
|
|
55
|
+
// skipped deliberately — it only emits a body for data-input
|
|
56
|
+
// ops, whereas every GraphQL op posts one, including
|
|
57
|
+
// load/list/remove.
|
|
58
|
+
$ctx->spec->body = ($utility->graphql_body)($ctx);
|
|
59
|
+
$ctx->spec->path = '';
|
|
60
|
+
// prepare_query already copied the op's match arguments into
|
|
61
|
+
// the query string. Those same values are bound as operation
|
|
62
|
+
// variables, so leaving them would send /graphql?id=i1.
|
|
63
|
+
$ctx->spec->query = [];
|
|
64
|
+
$ctx->spec->headers['content-type'] = ProjectNameGraphql::CONTENT_TYPE;
|
|
65
|
+
} else {
|
|
66
|
+
$ctx->spec->body = ($utility->prepare_body)($ctx);
|
|
67
|
+
$ctx->spec->path = ($utility->prepare_path)($ctx);
|
|
68
|
+
}
|
|
51
69
|
|
|
52
70
|
if ($ctx->ctrl->explain) {
|
|
53
71
|
$ctx->ctrl->explain['spec'] = $ctx->spec;
|
|
@@ -23,6 +23,7 @@ require_once __DIR__ . '/MakeUrl.php';
|
|
|
23
23
|
require_once __DIR__ . '/Param.php';
|
|
24
24
|
require_once __DIR__ . '/PrepareAuth.php';
|
|
25
25
|
require_once __DIR__ . '/PrepareBody.php';
|
|
26
|
+
require_once __DIR__ . '/Graphql.php';
|
|
26
27
|
require_once __DIR__ . '/PrepareHeaders.php';
|
|
27
28
|
require_once __DIR__ . '/PrepareMethod.php';
|
|
28
29
|
require_once __DIR__ . '/PrepareParams.php';
|
|
@@ -59,6 +60,8 @@ ProjectNameUtility::setRegistrar(function (ProjectNameUtility $u): void {
|
|
|
59
60
|
$u->prepare_params = [ProjectNamePrepareParams::class, 'call'];
|
|
60
61
|
$u->prepare_path = [ProjectNamePreparePath::class, 'call'];
|
|
61
62
|
$u->prepare_query = [ProjectNamePrepareQuery::class, 'call'];
|
|
63
|
+
$u->graphql_body = [ProjectNameGraphql::class, 'body'];
|
|
64
|
+
$u->graphql_errors = [ProjectNameGraphql::class, 'errors'];
|
|
62
65
|
$u->result_basic = [ProjectNameResultBasic::class, 'call'];
|
|
63
66
|
$u->result_body = [ProjectNameResultBody::class, 'call'];
|
|
64
67
|
$u->result_headers = [ProjectNameResultHeaders::class, 'call'];
|
|
File without changes
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
import random
|
|
5
5
|
|
|
6
|
-
from utility.voxgig_struct import voxgig_struct as vs
|
|
7
|
-
from core.control import ProjectNameControl
|
|
8
|
-
from core.operation import ProjectNameOperation
|
|
9
|
-
from core.spec import ProjectNameSpec
|
|
10
|
-
from core.result import ProjectNameResult
|
|
11
|
-
from core.response import ProjectNameResponse
|
|
12
|
-
from core.error import ProjectNameError
|
|
13
|
-
from core.helpers import get_ctx_prop, to_map
|
|
6
|
+
from projectname_sdk.utility.voxgig_struct import voxgig_struct as vs
|
|
7
|
+
from projectname_sdk.core.control import ProjectNameControl
|
|
8
|
+
from projectname_sdk.core.operation import ProjectNameOperation
|
|
9
|
+
from projectname_sdk.core.spec import ProjectNameSpec
|
|
10
|
+
from projectname_sdk.core.result import ProjectNameResult
|
|
11
|
+
from projectname_sdk.core.response import ProjectNameResponse
|
|
12
|
+
from projectname_sdk.core.error import ProjectNameError
|
|
13
|
+
from projectname_sdk.core.helpers import get_ctx_prop, to_map
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class ProjectNameContext:
|