@voxgig/sdkgen 1.5.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cmp/AgentGuideContent.js +5 -0
- package/dist/cmp/AgentGuideContent.js.map +1 -1
- package/dist/cmp/Feature.js +13 -0
- package/dist/cmp/Feature.js.map +1 -1
- package/dist/helpers/canonType.d.ts +3 -1
- package/dist/helpers/canonType.js +44 -1
- package/dist/helpers/canonType.js.map +1 -1
- package/dist/helpers/packageMeta.js +7 -0
- package/dist/helpers/packageMeta.js.map +1 -1
- package/dist/sdkgen.d.ts +3 -3
- package/dist/sdkgen.js +4 -1
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -6
- package/project/.sdk/model/feature/paging.aontu +7 -0
- package/project/.sdk/model/target/py-data.aontu +64 -0
- package/project/.sdk/src/cmp/c/fragment/Main.fragment.c +89 -1
- package/project/.sdk/src/cmp/clojure/Main_clojure.ts +1 -0
- package/project/.sdk/src/cmp/csharp/fragment/Main.fragment.cs +97 -0
- package/project/.sdk/src/cmp/dart/fragment/Main.fragment.dart +78 -0
- package/project/.sdk/src/cmp/elixir/fragment/Main.fragment.ex +84 -0
- package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +87 -0
- package/project/.sdk/src/cmp/js/fragment/Main.fragment.js +75 -0
- package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +85 -0
- package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +4 -0
- package/project/.sdk/src/cmp/perl/fragment/Main.fragment.pm +80 -0
- package/project/.sdk/src/cmp/php/fragment/Main.fragment.php +86 -0
- package/project/.sdk/src/cmp/py/Entity_py.ts +9 -2
- package/project/.sdk/src/cmp/py/MainEntity_py.ts +1 -1
- package/project/.sdk/src/cmp/py/Main_py.ts +34 -6
- package/project/.sdk/src/cmp/py/Package_py.ts +6 -9
- package/project/.sdk/src/cmp/py/TestDirect_py.ts +2 -2
- package/project/.sdk/src/cmp/py/TestEntity_py.ts +3 -3
- package/project/.sdk/src/cmp/py/fragment/Entity.fragment.py +2 -2
- package/project/.sdk/src/cmp/py/fragment/EntityCreateOp.fragment.py +2 -2
- package/project/.sdk/src/cmp/py/fragment/EntityLoadOp.fragment.py +2 -2
- package/project/.sdk/src/cmp/py/fragment/EntityRemoveOp.fragment.py +2 -2
- package/project/.sdk/src/cmp/py/fragment/EntityUpdateOp.fragment.py +2 -2
- package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +75 -8
- package/project/.sdk/src/cmp/py-data/Main_py-data.ts +987 -0
- package/project/.sdk/src/cmp/rb/fragment/Main.fragment.rb +71 -0
- package/project/.sdk/src/cmp/rust/fragment/Main.fragment.rs +95 -0
- package/project/.sdk/src/cmp/swift/fragment/Main.fragment.swift +86 -0
- package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +75 -0
- package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +95 -0
- package/project/.sdk/tm/c/core/sdk.h +7 -0
- package/project/.sdk/tm/c/feature/paging.c +141 -2
- package/project/.sdk/tm/c/utility/graphql.c +178 -0
- package/project/.sdk/tm/c/utility/make_options.c +1 -1
- package/project/.sdk/tm/c/utility/make_response.c +6 -0
- package/project/.sdk/tm/c/utility/make_spec.c +23 -3
- package/project/.sdk/tm/clojure/src/sdk/client.clj +64 -1
- package/project/.sdk/tm/clojure/src/sdk/core.clj +126 -3
- package/project/.sdk/tm/clojure/src/sdk/features.clj +80 -14
- package/project/.sdk/tm/cpp/core/types.hpp +83 -0
- package/project/.sdk/tm/cpp/feature/paging.hpp +103 -1
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +152 -3
- package/project/.sdk/tm/csharp/core/Utility.cs +4 -0
- package/project/.sdk/tm/csharp/feature/PagingFeature.cs +118 -1
- package/project/.sdk/tm/csharp/utility/Graphql.cs +190 -0
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +1 -1
- package/project/.sdk/tm/csharp/utility/MakeResponse.cs +7 -0
- package/project/.sdk/tm/csharp/utility/MakeSpec.cs +21 -2
- package/project/.sdk/tm/csharp/utility/Register.cs +2 -0
- package/project/.sdk/tm/dart/lib/Point.dart +7 -0
- package/project/.sdk/tm/dart/lib/feature/paging/PagingFeature.dart +99 -4
- package/project/.sdk/tm/dart/lib/utility/GraphqlUtility.dart +162 -0
- package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +1 -1
- package/project/.sdk/tm/dart/lib/utility/MakeResponseUtility.dart +5 -0
- package/project/.sdk/tm/dart/lib/utility/MakeSpecUtility.dart +18 -2
- package/project/.sdk/tm/dart/lib/utility/PrepareMethodUtility.dart +6 -1
- package/project/.sdk/tm/dart/lib/utility/Utility.dart +9 -0
- package/project/.sdk/tm/elixir/lib/projectname/feature/paging.ex +136 -13
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +187 -3
- package/project/.sdk/tm/go/core/utility_type.go +2 -0
- package/project/.sdk/tm/go/feature/paging_feature.go +87 -1
- package/project/.sdk/tm/go/utility/graphql.go +168 -0
- package/project/.sdk/tm/go/utility/make_options.go +1 -1
- package/project/.sdk/tm/go/utility/make_response.go +6 -0
- package/project/.sdk/tm/go/utility/make_spec.go +19 -2
- package/project/.sdk/tm/go/utility/register.go +2 -0
- package/project/.sdk/tm/haskell/src/SdkFeatures.hs +146 -7
- package/project/.sdk/tm/haskell/src/SdkRuntime.hs +153 -3
- package/project/.sdk/tm/java/core/SdkClient.java +93 -0
- package/project/.sdk/tm/java/core/Utility.java +4 -0
- package/project/.sdk/tm/java/feature/PagingFeature.java +100 -1
- package/project/.sdk/tm/java/utility/Graphql.java +191 -0
- package/project/.sdk/tm/java/utility/MakeOptions.java +1 -1
- package/project/.sdk/tm/java/utility/MakeResponse.java +6 -0
- package/project/.sdk/tm/java/utility/MakeSpec.java +17 -2
- package/project/.sdk/tm/java/utility/Register.java +2 -0
- package/project/.sdk/tm/js/src/Point.js +5 -0
- package/project/.sdk/tm/js/src/feature/paging/PagingFeature.js +82 -3
- package/project/.sdk/tm/js/src/feature/test/TestFeature.js +14 -2
- package/project/.sdk/tm/js/src/utility/GraphqlUtility.js +148 -0
- package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +1 -1
- package/project/.sdk/tm/js/src/utility/MakeResponseUtility.js +7 -0
- package/project/.sdk/tm/js/src/utility/MakeSpecUtility.js +19 -2
- package/project/.sdk/tm/js/src/utility/Utility.js +4 -0
- package/project/.sdk/tm/kotlin/core/SdkClient.kt +84 -0
- package/project/.sdk/tm/kotlin/core/Utility.kt +2 -0
- package/project/.sdk/tm/kotlin/feature/PagingFeature.kt +92 -1
- package/project/.sdk/tm/kotlin/utility/Graphql.kt +145 -0
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +1 -1
- package/project/.sdk/tm/kotlin/utility/MakeReqRes.kt +6 -0
- package/project/.sdk/tm/kotlin/utility/MakeSpecUrl.kt +17 -2
- package/project/.sdk/tm/kotlin/utility/Register.kt +2 -0
- package/project/.sdk/tm/lean/src/SdkUtility.lean +134 -1
- package/project/.sdk/tm/lua/feature/paging_feature.lua +97 -2
- package/project/.sdk/tm/lua/utility/graphql.lua +163 -0
- package/project/.sdk/tm/lua/utility/make_options.lua +1 -1
- package/project/.sdk/tm/lua/utility/make_response.lua +6 -0
- package/project/.sdk/tm/lua/utility/make_spec.lua +18 -2
- package/project/.sdk/tm/lua/utility/register.lua +3 -0
- package/project/.sdk/tm/ocaml/sdk_features.ml +164 -20
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +143 -4
- package/project/.sdk/tm/ocaml/sdk_types.ml +2 -0
- package/project/.sdk/tm/perl/feature/paging_feature.pm +91 -3
- package/project/.sdk/tm/perl/utility/graphql.pm +150 -0
- package/project/.sdk/tm/perl/utility/make_options.pm +1 -1
- package/project/.sdk/tm/perl/utility/make_response.pm +6 -0
- package/project/.sdk/tm/perl/utility/make_spec.pm +20 -2
- package/project/.sdk/tm/perl/utility/register.pm +1 -0
- package/project/.sdk/tm/php/core/UtilityType.php +2 -0
- package/project/.sdk/tm/php/feature/PagingFeature.php +106 -4
- package/project/.sdk/tm/php/utility/Graphql.php +176 -0
- package/project/.sdk/tm/php/utility/MakeOptions.php +1 -1
- package/project/.sdk/tm/php/utility/MakeResponse.php +7 -0
- package/project/.sdk/tm/php/utility/MakeSpec.php +20 -2
- package/project/.sdk/tm/php/utility/Register.php +3 -0
- package/project/.sdk/tm/py/pkg/core/__init__.py +0 -0
- package/project/.sdk/tm/py/{core → pkg/core}/context.py +8 -8
- package/project/.sdk/tm/py/{core → pkg/core}/operation.py +1 -1
- package/project/.sdk/tm/py/{core → pkg/core}/response.py +1 -1
- package/project/.sdk/tm/py/{core → pkg/core}/result.py +1 -1
- package/project/.sdk/tm/py/{core → pkg/core}/utility_type.py +2 -0
- package/project/.sdk/tm/py/pkg/entity/__init__.py +0 -0
- package/project/.sdk/tm/py/pkg/feature/__init__.py +0 -0
- package/project/.sdk/tm/py/{feature → pkg/feature}/audit_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/cache_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/clienttrack_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/debug_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/idempotency_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/log_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/metrics_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/netsim_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/paging_feature.py +87 -6
- package/project/.sdk/tm/py/{feature → pkg/feature}/proxy_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/ratelimit_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/rbac_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/retry_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/streaming_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/telemetry_feature.py +1 -1
- package/project/.sdk/tm/py/{feature → pkg/feature}/test_feature.py +2 -2
- package/project/.sdk/tm/py/{feature → pkg/feature}/timeout_feature.py +1 -1
- package/project/.sdk/tm/py/pkg/utility/__init__.py +0 -0
- package/project/.sdk/tm/py/{utility → pkg/utility}/feature_init.py +1 -1
- package/project/.sdk/tm/py/{utility → pkg/utility}/fetcher.py +1 -1
- package/project/.sdk/tm/py/pkg/utility/graphql.py +145 -0
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_context.py +1 -1
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_error.py +5 -5
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_fetch_def.py +2 -2
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_options.py +2 -2
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_point.py +2 -2
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_request.py +2 -2
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_response.py +6 -0
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_spec.py +19 -4
- package/project/.sdk/tm/py/{utility → pkg/utility}/make_url.py +1 -1
- package/project/.sdk/tm/py/{utility → pkg/utility}/param.py +2 -2
- package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_auth.py +1 -1
- package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_headers.py +1 -1
- package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_method.py +1 -1
- package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_params.py +1 -1
- package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_path.py +1 -1
- package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_query.py +1 -1
- package/project/.sdk/tm/py/pkg/utility/register.py +71 -0
- package/project/.sdk/tm/py/{utility → pkg/utility}/transform_request.py +2 -2
- package/project/.sdk/tm/py/{utility → pkg/utility}/transform_response.py +2 -2
- package/project/.sdk/tm/py/test/feature_harness.py +6 -6
- package/project/.sdk/tm/py/test/runner.py +1 -1
- package/project/.sdk/tm/py/test/struct_runner.py +1 -1
- package/project/.sdk/tm/py/test/test_pipeline.py +5 -5
- package/project/.sdk/tm/py/test/test_primary_utility.py +8 -8
- package/project/.sdk/tm/py/test/test_struct_utility.py +3 -3
- package/project/.sdk/tm/py-data/LICENSE +22 -0
- package/project/.sdk/tm/py-data/Makefile +57 -0
- package/project/.sdk/tm/py-data/pkg/auth.py +131 -0
- package/project/.sdk/tm/py-data/pkg/fetch.py +178 -0
- package/project/.sdk/tm/py-data/pkg/frames.py +236 -0
- package/project/.sdk/tm/py-data/src/feature/README.md +8 -0
- package/project/.sdk/tm/py-data/src/feature/debug/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/idempotency/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/log/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/metrics/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/netsim/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/paging/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/proxy/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/ratelimit/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/rbac/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/retry/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/streaming/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/telemetry/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/test/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/src/feature/timeout/.gitkeep +0 -0
- package/project/.sdk/tm/py-data/test/__init__.py +0 -0
- package/project/.sdk/tm/py-data/test/test_auth.py +95 -0
- package/project/.sdk/tm/py-data/test/test_fetch.py +152 -0
- package/project/.sdk/tm/py-data/test/test_frames.py +250 -0
- package/project/.sdk/tm/rb/core/utility_type.rb +1 -0
- package/project/.sdk/tm/rb/feature/paging_feature.rb +89 -3
- package/project/.sdk/tm/rb/utility/graphql.rb +131 -0
- package/project/.sdk/tm/rb/utility/make_options.rb +1 -1
- package/project/.sdk/tm/rb/utility/make_response.rb +6 -0
- package/project/.sdk/tm/rb/utility/make_spec.rb +18 -2
- package/project/.sdk/tm/rb/utility/register.rb +3 -0
- package/project/.sdk/tm/rust/feature/paging.rs +124 -2
- package/project/.sdk/tm/rust/utility/graphql.rs +193 -0
- package/project/.sdk/tm/rust/utility/make_options.rs +1 -1
- package/project/.sdk/tm/rust/utility/make_response.rs +6 -0
- package/project/.sdk/tm/rust/utility/make_spec.rs +32 -4
- package/project/.sdk/tm/rust/utility/mod.rs +1 -0
- package/project/.sdk/tm/scala/core/SdkClient.scala +90 -0
- package/project/.sdk/tm/scala/core/Utility.scala +4 -0
- package/project/.sdk/tm/scala/feature/PagingFeature.scala +104 -3
- package/project/.sdk/tm/scala/utility/Graphql.scala +160 -0
- package/project/.sdk/tm/scala/utility/Make.scala +7 -1
- package/project/.sdk/tm/scala/utility/MakeSpec.scala +22 -2
- package/project/.sdk/tm/scala/utility/Register.scala +2 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Utility.swift +4 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/PagingFeature.swift +86 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Graphql.swift +142 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Make.swift +24 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +1 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Register.swift +2 -0
- package/project/.sdk/tm/ts/src/Point.ts +7 -0
- package/project/.sdk/tm/ts/src/feature/paging/PagingFeature.ts +82 -3
- package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +14 -2
- package/project/.sdk/tm/ts/src/utility/GraphqlUtility.ts +152 -0
- package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +1 -1
- package/project/.sdk/tm/ts/src/utility/MakeResponseUtility.ts +7 -0
- package/project/.sdk/tm/ts/src/utility/MakeSpecUtility.ts +19 -2
- package/project/.sdk/tm/ts/src/utility/Utility.ts +4 -0
- package/project/.sdk/tm/zig/core/utility.zig +198 -3
- package/project/.sdk/tm/zig/feature/paging.zig +128 -1
- package/src/cmp/AgentGuideContent.ts +5 -0
- package/src/cmp/Feature.ts +13 -0
- package/src/helpers/canonType.ts +46 -0
- package/src/helpers/packageMeta.ts +7 -0
- package/src/sdkgen.ts +6 -1
- package/project/.sdk/tm/py/utility/register.py +0 -68
- /package/project/.sdk/tm/py/{core → pkg/core}/control.py +0 -0
- /package/project/.sdk/tm/py/{core → pkg/core}/error.py +0 -0
- /package/project/.sdk/tm/py/{core → pkg/core}/helpers.py +0 -0
- /package/project/.sdk/tm/py/{core → pkg/core}/spec.py +0 -0
- /package/project/.sdk/tm/py/{feature → pkg/feature}/base_feature.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/clean.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/done.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/feature_add.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/feature_hook.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/make_result.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_body.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/result_basic.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/result_body.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/result_headers.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/voxgig_struct/__init__.py +0 -0
- /package/project/.sdk/tm/py/{utility → pkg/utility}/voxgig_struct/voxgig_struct.py +0 -0
- /package/project/.sdk/tm/{py/core/__init__.py → py-data/src/feature/audit/.gitkeep} +0 -0
- /package/project/.sdk/tm/{py/entity/__init__.py → py-data/src/feature/base/.gitkeep} +0 -0
- /package/project/.sdk/tm/{py/feature/__init__.py → py-data/src/feature/cache/.gitkeep} +0 -0
- /package/project/.sdk/tm/{py/utility/__init__.py → py-data/src/feature/clienttrack/.gitkeep} +0 -0
|
@@ -415,6 +415,136 @@ prepareBodyUtil ctx = do
|
|
|
415
415
|
op <- readIORef (cOp ctx)
|
|
416
416
|
if opInput op == "data" then transformRequestUtil ctx else pure VNoval
|
|
417
417
|
|
|
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
|
|
423
|
+
-- operations exist and what each one's document is — is model data, computed
|
|
424
|
+
-- once by apidef and emitted into Config.
|
|
425
|
+
--
|
|
426
|
+
-- Two jobs:
|
|
427
|
+
--
|
|
428
|
+
-- graphqlBodyUtil — build { query, variables } for a point, binding the
|
|
429
|
+
-- op's arguments to the document's declared variables.
|
|
430
|
+
--
|
|
431
|
+
-- graphqlErrorsUtil — 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 resultBasic
|
|
434
|
+
-- never sees them.
|
|
435
|
+
-- ------------------------------------------------------------------
|
|
436
|
+
|
|
437
|
+
-- Content type every GraphQL-over-HTTP request uses.
|
|
438
|
+
graphqlContentType :: String
|
|
439
|
+
graphqlContentType = "application/json"
|
|
440
|
+
|
|
441
|
+
-- Map a GraphQL error to the same error codes the HTTP path produces, so a
|
|
442
|
+
-- caller handles auth or rate limiting identically on both transports.
|
|
443
|
+
-- Servers put the machine-readable code in `extensions.code`; Linear-style
|
|
444
|
+
-- APIs use `extensions.type`.
|
|
445
|
+
graphqlErrorCode :: Value -> IO String
|
|
446
|
+
graphqlErrorCode gqlerr = do
|
|
447
|
+
ext <- getp gqlerr "extensions"
|
|
448
|
+
c0 <- getStrD ext "code" ""
|
|
449
|
+
code <- if null c0 then getStrD ext "type" "" else pure c0
|
|
450
|
+
let raw = map toUpper code
|
|
451
|
+
pure $
|
|
452
|
+
if substrContains raw "AUTH" || substrContains raw "FORBIDDEN"
|
|
453
|
+
|| substrContains raw "UNAUTHENTICATED"
|
|
454
|
+
then "request_auth"
|
|
455
|
+
else if substrContains raw "RATELIMIT" || substrContains raw "RATE_LIMIT"
|
|
456
|
+
|| substrContains raw "TOO_MANY"
|
|
457
|
+
then "request_ratelimit"
|
|
458
|
+
else if substrContains raw "BAD_USER_INPUT"
|
|
459
|
+
|| substrContains raw "VALIDATION"
|
|
460
|
+
|| substrContains raw "INVALID"
|
|
461
|
+
then "request_invalid"
|
|
462
|
+
else "request_graphql"
|
|
463
|
+
|
|
464
|
+
-- Build the request body for a GraphQL point.
|
|
465
|
+
--
|
|
466
|
+
-- Variables come from the op's own arguments: a named variable binds to the
|
|
467
|
+
-- like-named argument (`from`), and the input-object variable (empty `from`)
|
|
468
|
+
-- takes the request data as a whole — which is what makes a generated
|
|
469
|
+
-- create/update call look exactly like its REST equivalent.
|
|
470
|
+
graphqlBodyUtil :: Context -> IO Value
|
|
471
|
+
graphqlBodyUtil ctx = do
|
|
472
|
+
point <- readIORef (cPoint ctx)
|
|
473
|
+
gql <- getp point "graphql"
|
|
474
|
+
case gql of
|
|
475
|
+
VMap _ -> do
|
|
476
|
+
-- reqmatch/reqdata hold the caller's arguments for THIS call;
|
|
477
|
+
-- data/match hold the entity's current state. Which pair depends on
|
|
478
|
+
-- whether the op takes match or data input. A named variable falls
|
|
479
|
+
-- back to the current state, so updating a loaded entity with just
|
|
480
|
+
-- {title} still binds the stored id the mutation requires.
|
|
481
|
+
op <- readIORef (cOp ctx)
|
|
482
|
+
let datainput = opInput op == "data"
|
|
483
|
+
rsV <- readIORef (if datainput then cReqdata ctx else cReqmatch ctx)
|
|
484
|
+
dsV <- readIORef (if datainput then cData ctx else cMatch ctx)
|
|
485
|
+
reqsrc <- case rsV of VMap _ -> pure rsV; _ -> emptyMap
|
|
486
|
+
datasrc <- case dsV of VMap _ -> pure dsV; _ -> emptyMap
|
|
487
|
+
variables <- emptyMap
|
|
488
|
+
vl <- getp gql "vars"
|
|
489
|
+
varlist <- case vl of VList _ -> listItems vl; _ -> pure []
|
|
490
|
+
forM_ varlist $ \spec -> case spec of
|
|
491
|
+
VMap _ -> do
|
|
492
|
+
name <- getStrD spec "name" ""
|
|
493
|
+
from <- getStrD spec "from" ""
|
|
494
|
+
when (not (null name)) $
|
|
495
|
+
if null from
|
|
496
|
+
then do
|
|
497
|
+
-- The input object IS the request body. Strip the action
|
|
498
|
+
-- selector, which is an SDK-side point discriminator, not an
|
|
499
|
+
-- API field.
|
|
500
|
+
body <- emptyMap
|
|
501
|
+
ks <- keysof reqsrc
|
|
502
|
+
forM_ ks $ \k -> when (k /= "$action") $ do
|
|
503
|
+
v <- getp reqsrc k
|
|
504
|
+
setp body k v
|
|
505
|
+
setp variables name body
|
|
506
|
+
else do
|
|
507
|
+
-- Only send variables the caller actually supplied: sending
|
|
508
|
+
-- an explicit null would clear a field on many APIs.
|
|
509
|
+
v0 <- getp reqsrc from
|
|
510
|
+
val <- if isNullish v0 then getp datasrc from else pure v0
|
|
511
|
+
when (not (isNullish val)) (setp variables name val)
|
|
512
|
+
_ -> pure ()
|
|
513
|
+
doc <- getp gql "doc"
|
|
514
|
+
jo [("query", doc), ("variables", variables)]
|
|
515
|
+
_ -> pure VNoval
|
|
516
|
+
|
|
517
|
+
-- Inspect a decoded GraphQL response body and record a failure when the
|
|
518
|
+
-- server reported one. Returns True when an error was recorded.
|
|
519
|
+
--
|
|
520
|
+
-- Partial data (`data` alongside `errors`) is treated as failure: the REST
|
|
521
|
+
-- surface has no partial-success concept, and silently returning half an
|
|
522
|
+
-- object would be worse than failing.
|
|
523
|
+
graphqlErrorsUtil :: Context -> IO Bool
|
|
524
|
+
graphqlErrorsUtil ctx = do
|
|
525
|
+
resultV <- readIORef (cResult ctx)
|
|
526
|
+
point <- readIORef (cPoint ctx)
|
|
527
|
+
kind <- getStrD point "kind" ""
|
|
528
|
+
case resultV of
|
|
529
|
+
VMap _ | kind == "graphql" -> do
|
|
530
|
+
body <- getp resultV "body"
|
|
531
|
+
ev <- getp body "errors"
|
|
532
|
+
errors <- case ev of VList _ -> listItems ev; _ -> pure []
|
|
533
|
+
if null errors
|
|
534
|
+
then pure False
|
|
535
|
+
else do
|
|
536
|
+
let firsterr = head errors
|
|
537
|
+
n = length errors
|
|
538
|
+
m0 <- getStrD firsterr "message" ""
|
|
539
|
+
let m1 = if null m0 then "graphql error" else m0
|
|
540
|
+
msg = if 1 < n then m1 ++ " (+" ++ show (n - 1) ++ " more)" else m1
|
|
541
|
+
code <- graphqlErrorCode firsterr
|
|
542
|
+
e <- mkErr code ("graphql: " ++ msg)
|
|
543
|
+
setp resultV "err" e
|
|
544
|
+
setp resultV "ok" (VBool False)
|
|
545
|
+
pure True
|
|
546
|
+
_ -> pure False
|
|
547
|
+
|
|
418
548
|
prepareAuthUtil :: Context -> UResult
|
|
419
549
|
prepareAuthUtil ctx = do
|
|
420
550
|
specV <- readIORef (cSpec ctx)
|
|
@@ -642,8 +772,23 @@ makeSpecUtil ctx = do
|
|
|
642
772
|
params <- prepareParamsUtil ctx; setp sp "params" params
|
|
643
773
|
query <- prepareQueryUtil ctx; setp sp "query" query
|
|
644
774
|
headers <- prepareHeadersUtil ctx; setp sp "headers" headers
|
|
645
|
-
|
|
646
|
-
|
|
775
|
+
kind <- getStrD point "kind" ""
|
|
776
|
+
if kind == "graphql"
|
|
777
|
+
-- GraphQL addresses one endpoint: no path parts, no query string,
|
|
778
|
+
-- and the body carries the operation. prepareBody is skipped
|
|
779
|
+
-- deliberately — it only emits a body for data-input ops, whereas
|
|
780
|
+
-- every GraphQL op posts one, including load/list/remove.
|
|
781
|
+
then do
|
|
782
|
+
body <- graphqlBodyUtil ctx; setp sp "body" body
|
|
783
|
+
setp sp "path" (VStr "")
|
|
784
|
+
-- prepareQuery already copied the op's match arguments into the
|
|
785
|
+
-- query string. Those same values are bound as operation
|
|
786
|
+
-- variables, so leaving them would send /graphql?id=i1.
|
|
787
|
+
emptyq <- emptyMap; setp sp "query" emptyq
|
|
788
|
+
setp headers "content-type" (VStr graphqlContentType)
|
|
789
|
+
else do
|
|
790
|
+
body <- prepareBodyUtil ctx; setp sp "body" body
|
|
791
|
+
path <- preparePathUtil ctx; setp sp "path" (VStr path)
|
|
647
792
|
ctrl <- readIORef (cCtrl ctx)
|
|
648
793
|
explain <- getp ctrl "explain"
|
|
649
794
|
case explain of { VMap _ -> do { snap <- specToValue sp; setp explain "spec" snap }; _ -> pure () }
|
|
@@ -780,6 +925,11 @@ makeResponseUtil ctx = do
|
|
|
780
925
|
VMap _ -> do
|
|
781
926
|
setp specV "step" (VStr "response")
|
|
782
927
|
resultBasicUtil ctx; resultHeadersUtil ctx; resultBodyUtil ctx
|
|
928
|
+
-- GraphQL reports failures as a top-level `errors` array under
|
|
929
|
+
-- HTTP 200, so resultBasic's status check never sees them. Lift
|
|
930
|
+
-- them here, before the response transform tries to unwrap data
|
|
931
|
+
-- that is not there.
|
|
932
|
+
_ <- graphqlErrorsUtil ctx
|
|
783
933
|
_ <- transformResponseUtil ctx
|
|
784
934
|
errv <- getp resultV "err"
|
|
785
935
|
isErrR <- isErr errv
|
|
@@ -866,7 +1016,7 @@ optSpecValue :: IO Value
|
|
|
866
1016
|
optSpecValue = do
|
|
867
1017
|
auth <- jo [("prefix", VStr "")]
|
|
868
1018
|
hdrs <- jo [("`$CHILD`", VStr "`$STRING`")]
|
|
869
|
-
allow <- jo [("method", VStr "GET,PUT,POST,PATCH,DELETE,OPTIONS"), ("op", VStr "create,update,load,list,remove,command,direct")]
|
|
1019
|
+
allow <- jo [("method", VStr "GET,PUT,POST,PATCH,DELETE,OPTIONS"), ("op", VStr "create,update,load,list,remove,command,direct,graphql")]
|
|
870
1020
|
entChild <- do a <- emptyMap; jo [("`$OPEN`", VBool True), ("active", VBool False), ("alias", a)]
|
|
871
1021
|
ent <- jo [("`$CHILD`", entChild)]
|
|
872
1022
|
featChild <- jo [("`$OPEN`", VBool True), ("active", VBool False)]
|
|
@@ -176,7 +176,100 @@ public abstract class SdkClient {
|
|
|
176
176
|
return utility.makeFetchDef.apply(ctx);
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Raw endpoint access is operator-controllable, like every entity op.
|
|
181
|
+
* Blocking it means denying BOTH the 'direct' and 'graphql' tokens, since
|
|
182
|
+
* either one reaches the same endpoint.
|
|
183
|
+
*/
|
|
179
184
|
public Map<String, Object> direct(Map<String, Object> fetchargs) {
|
|
185
|
+
if (!opAllowed("direct")) {
|
|
186
|
+
return opDenied("direct");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return rawRequest(fetchargs);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Is this raw-access op permitted by the SDK's allow.op option? */
|
|
193
|
+
private boolean opAllowed(String op) {
|
|
194
|
+
Object allow = Struct.getpath(this.options, List.of("allow", "op"));
|
|
195
|
+
return allow instanceof String && ((String) allow).contains(op);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private Map<String, Object> opDenied(String op) {
|
|
199
|
+
Object allow = Struct.getpath(this.options, List.of("allow", "op"));
|
|
200
|
+
Map<String, Object> out = new LinkedHashMap<>();
|
|
201
|
+
out.put("ok", false);
|
|
202
|
+
out.put("err", new SdkError(op + "_allow",
|
|
203
|
+
"ProjectNameSDK: " + op + ": operation not allowed by"
|
|
204
|
+
+ " SDK option allow.op value: \""
|
|
205
|
+
+ (allow instanceof String ? allow : "") + "\"", null));
|
|
206
|
+
return out;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Raw GraphQL access: the pressure valve that makes the generated
|
|
211
|
+
* surface's deliberate omissions (per-call selection sets, typed filter
|
|
212
|
+
* builders, batching, subscriptions) livable — the whole schema stays
|
|
213
|
+
* reachable.
|
|
214
|
+
*
|
|
215
|
+
* <p>Thin wrapper over the same prepare/fetch path direct uses, with the
|
|
216
|
+
* one thing raw direct cannot do for GraphQL: a GraphQL failure rides HTTP
|
|
217
|
+
* 200 as a top-level {@code errors} array, so status alone would report a
|
|
218
|
+
* failed query as ok.
|
|
219
|
+
*
|
|
220
|
+
* <p>NOTE: like direct, this bypasses the feature pipeline — no retry,
|
|
221
|
+
* ratelimit or paging features apply.
|
|
222
|
+
*/
|
|
223
|
+
public Map<String, Object> graphql(String query,
|
|
224
|
+
Map<String, Object> variables, Map<String, Object> ctrl) {
|
|
225
|
+
|
|
226
|
+
if (!opAllowed("graphql")) {
|
|
227
|
+
return opDenied("graphql");
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
Map<String, Object> headers = new LinkedHashMap<>();
|
|
231
|
+
headers.put("content-type", "application/json");
|
|
232
|
+
|
|
233
|
+
Map<String, Object> body = new LinkedHashMap<>();
|
|
234
|
+
body.put("query", query);
|
|
235
|
+
body.put("variables", variables == null ? new LinkedHashMap<>() : variables);
|
|
236
|
+
|
|
237
|
+
Map<String, Object> fetchargs = new LinkedHashMap<>();
|
|
238
|
+
fetchargs.put("method", "POST");
|
|
239
|
+
fetchargs.put("headers", headers);
|
|
240
|
+
fetchargs.put("body", body);
|
|
241
|
+
fetchargs.put("ctrl", ctrl == null ? new LinkedHashMap<>() : ctrl);
|
|
242
|
+
|
|
243
|
+
Map<String, Object> res = rawRequest(fetchargs);
|
|
244
|
+
|
|
245
|
+
// Errors are read BEFORE any status check: a GraphQL parse or validation
|
|
246
|
+
// failure comes back as HTTP 400 carrying the standard { errors: [...] }
|
|
247
|
+
// body, and the raw path represents a non-2xx as ok:false with no err —
|
|
248
|
+
// so returning early on status would discard the server's own
|
|
249
|
+
// diagnostics, which are the only useful part of that response.
|
|
250
|
+
Object errors = Struct.getpath(res, List.of("data", "errors"));
|
|
251
|
+
|
|
252
|
+
if (errors instanceof List && !((List<Object>) errors).isEmpty()) {
|
|
253
|
+
Object first = ((List<Object>) errors).get(0);
|
|
254
|
+
Object m = Struct.getprop(first, "message");
|
|
255
|
+
String msg = m instanceof String && !((String) m).isEmpty()
|
|
256
|
+
? (String) m : "graphql error";
|
|
257
|
+
res.put("ok", false);
|
|
258
|
+
res.put("err", new SdkError("graphql_error",
|
|
259
|
+
"ProjectNameSDK: graphql: " + msg, null));
|
|
260
|
+
res.put("graphql", errors);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return res;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Ungated request path shared by direct and graphql, each of which checks
|
|
268
|
+
* its own allow.op token first. Private, rather than a flag on fetchargs:
|
|
269
|
+
* a caller-supplied marker would let anyone opt straight back out of the
|
|
270
|
+
* gate by passing it.
|
|
271
|
+
*/
|
|
272
|
+
private Map<String, Object> rawRequest(Map<String, Object> fetchargs) {
|
|
180
273
|
Utility utility = this.utility;
|
|
181
274
|
|
|
182
275
|
if (fetchargs == null) {
|
|
@@ -74,6 +74,8 @@ public class Utility {
|
|
|
74
74
|
public CtxFn<Map<String, Object>> prepareParams;
|
|
75
75
|
public CtxFn<String> preparePath;
|
|
76
76
|
public CtxFn<Map<String, Object>> prepareQuery;
|
|
77
|
+
public CtxFn<Object> graphqlBody;
|
|
78
|
+
public CtxFn<Result> graphqlErrors;
|
|
77
79
|
public CtxFn<Result> resultBasic;
|
|
78
80
|
public CtxFn<Result> resultBody;
|
|
79
81
|
public CtxFn<Result> resultHeaders;
|
|
@@ -115,6 +117,8 @@ public class Utility {
|
|
|
115
117
|
u.prepareParams = this.prepareParams;
|
|
116
118
|
u.preparePath = this.preparePath;
|
|
117
119
|
u.prepareQuery = this.prepareQuery;
|
|
120
|
+
u.graphqlBody = this.graphqlBody;
|
|
121
|
+
u.graphqlErrors = this.graphqlErrors;
|
|
118
122
|
u.resultBasic = this.resultBasic;
|
|
119
123
|
u.resultBody = this.resultBody;
|
|
120
124
|
u.resultHeaders = this.resultHeaders;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
package JAVAPACKAGE.feature;
|
|
2
2
|
|
|
3
3
|
import java.util.LinkedHashMap;
|
|
4
|
+
import java.util.LinkedHashSet;
|
|
4
5
|
import java.util.List;
|
|
6
|
+
import java.util.Set;
|
|
5
7
|
import java.util.Map;
|
|
6
8
|
import java.util.regex.Matcher;
|
|
7
9
|
import java.util.regex.Pattern;
|
|
@@ -10,6 +12,7 @@ import JAVAPACKAGE.core.Context;
|
|
|
10
12
|
import JAVAPACKAGE.core.Result;
|
|
11
13
|
import JAVAPACKAGE.core.SdkClient;
|
|
12
14
|
import JAVAPACKAGE.core.Spec;
|
|
15
|
+
import JAVAPACKAGE.utility.struct.Struct;
|
|
13
16
|
|
|
14
17
|
// Pagination support for list operations. On the way out (PreRequest) it
|
|
15
18
|
// stamps page/limit (or a cursor) into the request query; on the way back
|
|
@@ -66,6 +69,14 @@ public class PagingFeature extends BaseFeature {
|
|
|
66
69
|
paging = new LinkedHashMap<>();
|
|
67
70
|
}
|
|
68
71
|
|
|
72
|
+
// GraphQL paginates through operation VARIABLES, not the query string.
|
|
73
|
+
// This hook runs after makeSpec, so spec.body already holds the
|
|
74
|
+
// { query, variables } envelope, and before makeFetchDef serialises it.
|
|
75
|
+
if ("graphql".equals(Struct.getprop(ctx.point, "kind", null))) {
|
|
76
|
+
graphqlPreRequest(ctx, paging);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
69
80
|
Object cursor = paging.get("cursor");
|
|
70
81
|
if (cursor != null) {
|
|
71
82
|
spec.query.put(cursorParam, cursor);
|
|
@@ -85,6 +96,51 @@ public class PagingFeature extends BaseFeature {
|
|
|
85
96
|
}
|
|
86
97
|
}
|
|
87
98
|
|
|
99
|
+
// Relay pagination: the cursor is the `after` variable (or whatever the
|
|
100
|
+
// model named it), and the page size is `first`.
|
|
101
|
+
@SuppressWarnings("unchecked")
|
|
102
|
+
private void graphqlPreRequest(Context ctx, Map<String, Object> paging) {
|
|
103
|
+
if (!(ctx.spec.body instanceof Map)) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
Map<String, Object> body = (Map<String, Object>) ctx.spec.body;
|
|
107
|
+
|
|
108
|
+
Object varsval = body.get("variables");
|
|
109
|
+
Map<String, Object> variables;
|
|
110
|
+
if (varsval instanceof Map) {
|
|
111
|
+
variables = (Map<String, Object>) varsval;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
variables = new LinkedHashMap<>();
|
|
115
|
+
body.put("variables", variables);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
String afterVar = FeatureOptions.foptStr(this.options, "afterVar", "after");
|
|
119
|
+
String firstVar = FeatureOptions.foptStr(this.options, "firstVar", "first");
|
|
120
|
+
|
|
121
|
+
// Only bind variables the operation actually declares, or the server
|
|
122
|
+
// rejects the document.
|
|
123
|
+
Set<String> declared = new LinkedHashSet<>();
|
|
124
|
+
Object varlist = Struct.getpath(ctx.point, List.of("graphql", "vars"));
|
|
125
|
+
if (varlist instanceof List) {
|
|
126
|
+
for (Object v : (List<Object>) varlist) {
|
|
127
|
+
Object name = Struct.getprop(v, "name", null);
|
|
128
|
+
if (name instanceof String) {
|
|
129
|
+
declared.add((String) name);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (paging.get("cursor") != null && declared.contains(afterVar)) {
|
|
135
|
+
variables.put(afterVar, paging.get("cursor"));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (this.options.get("limit") != null && variables.get(firstVar) == null
|
|
139
|
+
&& declared.contains(firstVar)) {
|
|
140
|
+
variables.put(firstVar, FeatureOptions.foptInt(this.options, "limit", 0));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
88
144
|
@Override
|
|
89
145
|
public void preResult(Context ctx) {
|
|
90
146
|
if (!this.active || !isList(ctx)) {
|
|
@@ -113,6 +169,43 @@ public class PagingFeature extends BaseFeature {
|
|
|
113
169
|
}
|
|
114
170
|
}
|
|
115
171
|
|
|
172
|
+
// Set when the response states hasMore outright, rather than leaving it
|
|
173
|
+
// to be inferred from the presence of a cursor.
|
|
174
|
+
boolean explicitMore = false;
|
|
175
|
+
|
|
176
|
+
// Relay connections carry the cursor in pageInfo, at the path the model
|
|
177
|
+
// recorded for this op.
|
|
178
|
+
Object page = Struct.getpath(ctx.point, List.of("graphql", "page"));
|
|
179
|
+
if (page instanceof Map && body instanceof Map) {
|
|
180
|
+
// `connpath` locates the connection object inside the response
|
|
181
|
+
// envelope (data.<field>); the cursor/more paths are relative to it.
|
|
182
|
+
Object connpath = Struct.getprop(page, "connpath", null);
|
|
183
|
+
Object conn = body;
|
|
184
|
+
if (connpath instanceof String && !"".equals(connpath)) {
|
|
185
|
+
Object sub = Struct.getpath(body, connpath);
|
|
186
|
+
if (sub != null) {
|
|
187
|
+
conn = sub;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
Object cursorpath = Struct.getprop(page, "cursor", null);
|
|
192
|
+
if (cursorpath instanceof String && !"".equals(cursorpath)) {
|
|
193
|
+
Object cursor = Struct.getpath(conn, cursorpath);
|
|
194
|
+
if (cursor != null) {
|
|
195
|
+
paging.put("cursor", cursor);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
Object morepath = Struct.getprop(page, "more", null);
|
|
200
|
+
if (morepath instanceof String && !"".equals(morepath)) {
|
|
201
|
+
Object more = Struct.getpath(conn, morepath);
|
|
202
|
+
if (more instanceof Boolean) {
|
|
203
|
+
paging.put("hasMore", more);
|
|
204
|
+
explicitMore = true;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
116
209
|
// Body-level cursors.
|
|
117
210
|
if (body instanceof Map) {
|
|
118
211
|
Map<String, Object> bm = (Map<String, Object>) body;
|
|
@@ -127,10 +220,16 @@ public class PagingFeature extends BaseFeature {
|
|
|
127
220
|
}
|
|
128
221
|
if (bm.get("hasMore") instanceof Boolean) {
|
|
129
222
|
paging.put("hasMore", bm.get("hasMore"));
|
|
223
|
+
explicitMore = true;
|
|
130
224
|
}
|
|
131
225
|
}
|
|
132
226
|
|
|
133
|
-
|
|
227
|
+
// Cursor presence only INFERS another page. When the server stated the
|
|
228
|
+
// answer outright — relay's `hasNextPage: false`, or a body `hasMore` —
|
|
229
|
+
// that wins: a final page normally carries both an end cursor and
|
|
230
|
+
// hasNextPage false, and inferring from the cursor there would send the
|
|
231
|
+
// caller back for a page that does not exist, forever.
|
|
232
|
+
if (!explicitMore && !Boolean.TRUE.equals(paging.get("hasMore"))
|
|
134
233
|
&& (paging.get("next") != null || paging.get("cursor") != null
|
|
135
234
|
|| paging.get("nextPage") != null)) {
|
|
136
235
|
paging.put("hasMore", true);
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
package JAVAPACKAGE.utility;
|
|
2
|
+
|
|
3
|
+
import java.util.ArrayList;
|
|
4
|
+
import java.util.LinkedHashMap;
|
|
5
|
+
import java.util.List;
|
|
6
|
+
import java.util.Map;
|
|
7
|
+
|
|
8
|
+
import JAVAPACKAGE.core.Context;
|
|
9
|
+
import JAVAPACKAGE.core.Result;
|
|
10
|
+
import JAVAPACKAGE.utility.struct.Struct;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* GraphQL transport. API-INDEPENDENT: every GraphQL SDK this generator
|
|
14
|
+
* produces uses this file unchanged. The API-specific part — which
|
|
15
|
+
* operations exist and what each one's document is — is model data,
|
|
16
|
+
* computed once by apidef and emitted into Config.
|
|
17
|
+
*
|
|
18
|
+
* <p>Two jobs:
|
|
19
|
+
*
|
|
20
|
+
* <p>graphqlBody — build { query, variables } for a point, binding the op's
|
|
21
|
+
* arguments to the document's declared variables.
|
|
22
|
+
*
|
|
23
|
+
* <p>graphqlErrors — lift a GraphQL failure into an SDK error. GraphQL
|
|
24
|
+
* reports failures as a top-level {@code errors} array under HTTP 200, so
|
|
25
|
+
* the status-driven path in resultBasic never sees them.
|
|
26
|
+
*/
|
|
27
|
+
@SuppressWarnings({"unchecked"})
|
|
28
|
+
final class Graphql {
|
|
29
|
+
|
|
30
|
+
/** Content type every GraphQL-over-HTTP request uses. */
|
|
31
|
+
static final String GRAPHQL_CONTENT_TYPE = "application/json";
|
|
32
|
+
|
|
33
|
+
private Graphql() {}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Map a GraphQL error to the same error codes the HTTP path produces, so
|
|
37
|
+
* a caller handles auth or rate limiting identically on both transports.
|
|
38
|
+
* Servers put the machine-readable code in {@code extensions.code};
|
|
39
|
+
* Linear-style APIs use {@code extensions.type}.
|
|
40
|
+
*/
|
|
41
|
+
static String graphqlErrorCode(Object gqlerr) {
|
|
42
|
+
Object ext = Struct.getprop(gqlerr, "extensions", null);
|
|
43
|
+
|
|
44
|
+
Object code = Struct.getprop(ext, "code", null);
|
|
45
|
+
if (null == code || "".equals(code)) {
|
|
46
|
+
code = Struct.getprop(ext, "type", null);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
String raw = (code instanceof String) ? ((String) code).toUpperCase() : "";
|
|
50
|
+
|
|
51
|
+
if (raw.contains("AUTH") || raw.contains("FORBIDDEN")
|
|
52
|
+
|| raw.contains("UNAUTHENTICATED")) {
|
|
53
|
+
return "request_auth";
|
|
54
|
+
}
|
|
55
|
+
if (raw.contains("RATELIMIT") || raw.contains("RATE_LIMIT")
|
|
56
|
+
|| raw.contains("TOO_MANY")) {
|
|
57
|
+
return "request_ratelimit";
|
|
58
|
+
}
|
|
59
|
+
if (raw.contains("BAD_USER_INPUT") || raw.contains("VALIDATION")
|
|
60
|
+
|| raw.contains("INVALID")) {
|
|
61
|
+
return "request_invalid";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return "request_graphql";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Build the request body for a GraphQL point.
|
|
69
|
+
*
|
|
70
|
+
* <p>Variables come from the op's own arguments: a named variable binds to
|
|
71
|
+
* the like-named argument ({@code from}), and the input-object variable
|
|
72
|
+
* (empty {@code from}) takes the request data as a whole — which is what
|
|
73
|
+
* makes a generated create/update call look exactly like its REST
|
|
74
|
+
* equivalent.
|
|
75
|
+
*/
|
|
76
|
+
static Object graphqlBody(Context ctx) {
|
|
77
|
+
Object gql = Struct.getprop(ctx.point, "graphql", null);
|
|
78
|
+
|
|
79
|
+
if (!(gql instanceof Map)) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// reqmatch/reqdata hold the caller's arguments for THIS call; data/match
|
|
84
|
+
// hold the entity's current state. Which pair depends on whether the op
|
|
85
|
+
// takes match or data input. A named variable falls back to the current
|
|
86
|
+
// state, so updating a loaded entity with just {title} still binds the
|
|
87
|
+
// stored id the mutation requires.
|
|
88
|
+
Map<String, Object> reqsrc = ctx.reqmatch;
|
|
89
|
+
Map<String, Object> datasrc = ctx.match;
|
|
90
|
+
if (null != ctx.op && "data".equals(ctx.op.input)) {
|
|
91
|
+
reqsrc = ctx.reqdata;
|
|
92
|
+
datasrc = ctx.data;
|
|
93
|
+
}
|
|
94
|
+
if (null == reqsrc) {
|
|
95
|
+
reqsrc = new LinkedHashMap<>();
|
|
96
|
+
}
|
|
97
|
+
if (null == datasrc) {
|
|
98
|
+
datasrc = new LinkedHashMap<>();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
Map<String, Object> variables = new LinkedHashMap<>();
|
|
102
|
+
|
|
103
|
+
Object rawvars = Struct.getprop(gql, "vars", null);
|
|
104
|
+
List<Object> varlist =
|
|
105
|
+
(rawvars instanceof List) ? (List<Object>) rawvars : new ArrayList<>();
|
|
106
|
+
|
|
107
|
+
for (Object spec : varlist) {
|
|
108
|
+
if (!(spec instanceof Map)) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
Object nameval = Struct.getprop(spec, "name", null);
|
|
113
|
+
String name = (nameval instanceof String) ? (String) nameval : "";
|
|
114
|
+
|
|
115
|
+
Object fromval = Struct.getprop(spec, "from", null);
|
|
116
|
+
String from = (fromval instanceof String) ? (String) fromval : "";
|
|
117
|
+
|
|
118
|
+
if ("".equals(from)) {
|
|
119
|
+
// The input object IS the request body. Strip the action selector,
|
|
120
|
+
// which is an SDK-side point discriminator, not an API field.
|
|
121
|
+
Map<String, Object> body = new LinkedHashMap<>();
|
|
122
|
+
for (Map.Entry<String, Object> e : reqsrc.entrySet()) {
|
|
123
|
+
if (!"$action".equals(e.getKey())) {
|
|
124
|
+
body.put(e.getKey(), e.getValue());
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
variables.put(name, body);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Only send variables the caller actually supplied: sending an
|
|
132
|
+
// explicit null would clear a field on many APIs.
|
|
133
|
+
Object val = Struct.getprop(reqsrc, from, null);
|
|
134
|
+
if (null == val) {
|
|
135
|
+
val = Struct.getprop(datasrc, from, null);
|
|
136
|
+
}
|
|
137
|
+
if (null != val) {
|
|
138
|
+
variables.put(name, val);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
Map<String, Object> out = new LinkedHashMap<>();
|
|
143
|
+
out.put("query", Struct.getprop(gql, "doc", null));
|
|
144
|
+
out.put("variables", variables);
|
|
145
|
+
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Inspect a decoded GraphQL response body and record a failure when the
|
|
151
|
+
* server reported one. Returns true when an error was recorded.
|
|
152
|
+
*
|
|
153
|
+
* <p>Partial data ({@code data} alongside {@code errors}) is treated as
|
|
154
|
+
* failure: the REST surface has no partial-success concept, and silently
|
|
155
|
+
* returning half an object would be worse than failing. The raw envelope
|
|
156
|
+
* stays available on the result for callers that need it.
|
|
157
|
+
*/
|
|
158
|
+
static Result graphqlErrors(Context ctx) {
|
|
159
|
+
Result result = ctx.result;
|
|
160
|
+
|
|
161
|
+
if (null == result || null == ctx.point) {
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (!"graphql".equals(Struct.getprop(ctx.point, "kind", null))) {
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
Object rawerrors = Struct.getprop(result.body, "errors", null);
|
|
170
|
+
if (!(rawerrors instanceof List)) {
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
List<Object> errors = (List<Object>) rawerrors;
|
|
175
|
+
if (0 == errors.size()) {
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
Object first = errors.get(0);
|
|
180
|
+
Object msgval = Struct.getprop(first, "message", null);
|
|
181
|
+
String msg = (msgval instanceof String) ? (String) msgval : "graphql error";
|
|
182
|
+
if (1 < errors.size()) {
|
|
183
|
+
msg = msg + " (+" + (errors.size() - 1) + " more)";
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
result.err = ctx.makeError(graphqlErrorCode(first), "graphql: " + msg);
|
|
187
|
+
result.ok = false;
|
|
188
|
+
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -73,7 +73,7 @@ final class MakeOptions {
|
|
|
73
73
|
+ "\"headers\": { \"`$CHILD`\": \"`$STRING`\" },"
|
|
74
74
|
+ "\"allow\": {"
|
|
75
75
|
+ " \"method\": \"GET,PUT,POST,PATCH,DELETE,OPTIONS\","
|
|
76
|
-
+ " \"op\": \"create,update,load,list,remove,command,direct\""
|
|
76
|
+
+ " \"op\": \"create,update,load,list,remove,command,direct,graphql\""
|
|
77
77
|
+ "},"
|
|
78
78
|
+ "\"entity\": { \"`$CHILD`\": {"
|
|
79
79
|
+ " \"`$OPEN`\": true, \"active\": false, \"alias\": {} } },"
|
|
@@ -39,6 +39,12 @@ final class MakeResponse {
|
|
|
39
39
|
utility.resultBasic.apply(ctx);
|
|
40
40
|
utility.resultHeaders.apply(ctx);
|
|
41
41
|
utility.resultBody.apply(ctx);
|
|
42
|
+
|
|
43
|
+
// GraphQL reports failures as a top-level `errors` array under HTTP 200,
|
|
44
|
+
// so resultBasic's status check never sees them. Lift them here, before
|
|
45
|
+
// the response transform tries to unwrap data that is not there.
|
|
46
|
+
utility.graphqlErrors.apply(ctx);
|
|
47
|
+
|
|
42
48
|
utility.transformResponse.apply(ctx);
|
|
43
49
|
|
|
44
50
|
if (result.err == null) {
|