@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,162 @@
|
|
|
1
|
+
import 'voxgig_struct.dart' as vs;
|
|
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
|
+
// graphqlBody — build { query, variables } for a point, binding the
|
|
11
|
+
// op's arguments to the document's declared variables.
|
|
12
|
+
//
|
|
13
|
+
// graphqlErrors — 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 resultBasic
|
|
16
|
+
// never sees them.
|
|
17
|
+
|
|
18
|
+
// Content type every GraphQL-over-HTTP request uses.
|
|
19
|
+
const String graphqlContentType = 'application/json';
|
|
20
|
+
|
|
21
|
+
// Map a GraphQL error to the same error codes the HTTP path produces, so a
|
|
22
|
+
// caller handles auth or rate limiting identically on both transports.
|
|
23
|
+
// Servers put the machine-readable code in `extensions.code`; Linear-style
|
|
24
|
+
// APIs use `extensions.type`.
|
|
25
|
+
String graphqlErrorCode(dynamic gqlerr) {
|
|
26
|
+
final ext = vs.getprop(gqlerr, 'extensions');
|
|
27
|
+
|
|
28
|
+
var raw = vs.getprop(ext, 'code');
|
|
29
|
+
if (null == raw || '' == raw) {
|
|
30
|
+
raw = vs.getprop(ext, 'type');
|
|
31
|
+
}
|
|
32
|
+
final code = (raw is String) ? raw.toUpperCase() : '';
|
|
33
|
+
|
|
34
|
+
if (code.contains('AUTH') ||
|
|
35
|
+
code.contains('FORBIDDEN') ||
|
|
36
|
+
code.contains('UNAUTHENTICATED')) {
|
|
37
|
+
return 'request_auth';
|
|
38
|
+
}
|
|
39
|
+
if (code.contains('RATELIMIT') ||
|
|
40
|
+
code.contains('RATE_LIMIT') ||
|
|
41
|
+
code.contains('TOO_MANY')) {
|
|
42
|
+
return 'request_ratelimit';
|
|
43
|
+
}
|
|
44
|
+
if (code.contains('BAD_USER_INPUT') ||
|
|
45
|
+
code.contains('VALIDATION') ||
|
|
46
|
+
code.contains('INVALID')) {
|
|
47
|
+
return 'request_invalid';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return 'request_graphql';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Build the request body for a GraphQL point.
|
|
54
|
+
//
|
|
55
|
+
// Variables come from the op's own arguments: a named variable binds to the
|
|
56
|
+
// like-named argument (`from`), and the input-object variable (empty
|
|
57
|
+
// `from`) takes the request data as a whole — which is what makes a
|
|
58
|
+
// generated create/update call look exactly like its REST equivalent.
|
|
59
|
+
dynamic graphqlBody(dynamic ctx) {
|
|
60
|
+
final gql = ctx.point?.graphql;
|
|
61
|
+
|
|
62
|
+
if (gql is! Map) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// reqmatch/reqdata hold the caller's arguments for THIS call; data/match
|
|
67
|
+
// hold the entity's current state. Which pair depends on whether the op
|
|
68
|
+
// takes match or data input. A named variable falls back to the current
|
|
69
|
+
// state, so updating a loaded entity with just {title} still binds the
|
|
70
|
+
// stored id the mutation requires.
|
|
71
|
+
var reqsrc = ctx.reqmatch;
|
|
72
|
+
var datasrc = ctx.match;
|
|
73
|
+
if (null != ctx.op && 'data' == ctx.op.input) {
|
|
74
|
+
reqsrc = ctx.reqdata;
|
|
75
|
+
datasrc = ctx.data;
|
|
76
|
+
}
|
|
77
|
+
if (reqsrc is! Map) {
|
|
78
|
+
reqsrc = <String, dynamic>{};
|
|
79
|
+
}
|
|
80
|
+
if (datasrc is! Map) {
|
|
81
|
+
datasrc = <String, dynamic>{};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
final variables = <String, dynamic>{};
|
|
85
|
+
|
|
86
|
+
final varlist = vs.getprop(gql, 'vars');
|
|
87
|
+
if (varlist is List) {
|
|
88
|
+
for (final spec in varlist) {
|
|
89
|
+
if (spec is! Map) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
final name = vs.getprop(spec, 'name');
|
|
94
|
+
final from = vs.getprop(spec, 'from');
|
|
95
|
+
if (null == name) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (null == from || '' == from) {
|
|
100
|
+
// The input object IS the request body. Strip the action selector,
|
|
101
|
+
// which is an SDK-side point discriminator, not an API field.
|
|
102
|
+
final body = <String, dynamic>{};
|
|
103
|
+
reqsrc.forEach((k, v) {
|
|
104
|
+
if ('\$action' != k) {
|
|
105
|
+
body[k.toString()] = v;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
variables[name.toString()] = body;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Only send variables the caller actually supplied: sending an
|
|
113
|
+
// explicit null would clear a field on many APIs.
|
|
114
|
+
final val = vs.getprop(reqsrc, from) ?? vs.getprop(datasrc, from);
|
|
115
|
+
if (null != val) {
|
|
116
|
+
variables[name.toString()] = val;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
'query': vs.getprop(gql, 'doc'),
|
|
123
|
+
'variables': variables,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Inspect a decoded GraphQL response body and record a failure when the
|
|
128
|
+
// server reported one. Returns true when an error was recorded.
|
|
129
|
+
//
|
|
130
|
+
// Partial data (`data` alongside `errors`) is treated as failure: the REST
|
|
131
|
+
// surface has no partial-success concept, and silently returning half an
|
|
132
|
+
// object would be worse than failing.
|
|
133
|
+
bool graphqlErrors(dynamic ctx) {
|
|
134
|
+
final result = ctx.result;
|
|
135
|
+
|
|
136
|
+
if (null == result || null == ctx.point) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if ('graphql' != ctx.point.kind) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
final errors = vs.getprop(result.body, 'errors');
|
|
145
|
+
if (errors is! List || 0 == errors.length) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
final first = errors[0];
|
|
150
|
+
var msg = vs.getprop(first, 'message');
|
|
151
|
+
if (msg is! String || '' == msg) {
|
|
152
|
+
msg = 'graphql error';
|
|
153
|
+
}
|
|
154
|
+
if (1 < errors.length) {
|
|
155
|
+
msg = msg + ' (+' + (errors.length - 1).toString() + ' more)';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
result.err = ctx.error(graphqlErrorCode(first), 'graphql: ' + msg);
|
|
159
|
+
result.ok = false;
|
|
160
|
+
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
@@ -29,7 +29,7 @@ dynamic makeOptions(dynamic ctx) {
|
|
|
29
29
|
},
|
|
30
30
|
'allow': {
|
|
31
31
|
'method': 'GET,PUT,POST,PATCH,DELETE,OPTIONS',
|
|
32
|
-
'op': 'create,update,load,list,remove,command,direct',
|
|
32
|
+
'op': 'create,update,load,list,remove,command,direct,graphql',
|
|
33
33
|
},
|
|
34
34
|
'entity': {
|
|
35
35
|
'`\$CHILD`': {
|
|
@@ -37,6 +37,11 @@ Future<dynamic> makeResponse(dynamic ctx) async {
|
|
|
37
37
|
resultBasic(ctx);
|
|
38
38
|
resultHeaders(ctx);
|
|
39
39
|
await Future.value(resultBody(ctx));
|
|
40
|
+
|
|
41
|
+
// GraphQL reports failures as a top-level `errors` array under HTTP
|
|
42
|
+
// 200, so resultBasic's status check never sees them. Lift them here,
|
|
43
|
+
// before the response transform tries to unwrap data that is not there.
|
|
44
|
+
utility.graphqlErrors(ctx);
|
|
40
45
|
transformResponse(ctx);
|
|
41
46
|
|
|
42
47
|
if (null == result.err) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import 'GraphqlUtility.dart';
|
|
1
2
|
import '../Spec.dart';
|
|
2
3
|
|
|
3
4
|
import 'ErrUtility.dart';
|
|
@@ -44,8 +45,23 @@ dynamic makeSpec(dynamic ctx) {
|
|
|
44
45
|
ctx.spec.params = prepareParams(ctx);
|
|
45
46
|
ctx.spec.query = prepareQuery(ctx);
|
|
46
47
|
ctx.spec.headers = prepareHeaders(ctx);
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
|
|
49
|
+
if ('graphql' == point.kind) {
|
|
50
|
+
// GraphQL addresses one endpoint: no path parts, no query string, and
|
|
51
|
+
// the body carries the operation. prepareBody is skipped deliberately
|
|
52
|
+
// — it only emits a body for data-input ops, whereas every GraphQL op
|
|
53
|
+
// posts one, including load/list/remove.
|
|
54
|
+
ctx.spec.body = utility.graphqlBody(ctx);
|
|
55
|
+
ctx.spec.path = '';
|
|
56
|
+
// prepareQuery already copied the op's match arguments into the query
|
|
57
|
+
// string. Those same values are bound as operation variables, so
|
|
58
|
+
// leaving them would send /graphql?id=i1.
|
|
59
|
+
ctx.spec.query = <String, dynamic>{};
|
|
60
|
+
ctx.spec.headers['content-type'] = graphqlContentType;
|
|
61
|
+
} else {
|
|
62
|
+
ctx.spec.body = prepareBody(ctx);
|
|
63
|
+
ctx.spec.path = preparePath(ctx);
|
|
64
|
+
}
|
|
49
65
|
|
|
50
66
|
if (null != ctx.ctrl['explain']) {
|
|
51
67
|
ctx.ctrl['explain']['spec'] = ctx.spec;
|
|
@@ -16,7 +16,12 @@ dynamic prepareMethod(dynamic ctx) {
|
|
|
16
16
|
// The API definition is authoritative: a POST-only or PATCH-based API
|
|
17
17
|
// exposes `update` as POST or PATCH, not the PUT the op name implies.
|
|
18
18
|
// Only fall back to the op-name convention when the point has no method.
|
|
19
|
-
|
|
19
|
+
// Read the field off the Point object directly: vs.getprop only handles
|
|
20
|
+
// maps and lists, so going through it always missed the point's method and
|
|
21
|
+
// fell back to the op-name convention below. A GraphQL point synthesizes
|
|
22
|
+
// POST for every op, and a POST-only or PATCH-based REST API states its own
|
|
23
|
+
// method too — both were being overridden.
|
|
24
|
+
final method = ctx.point?.method;
|
|
20
25
|
if (method is String && '' != method) {
|
|
21
26
|
return method.toUpperCase();
|
|
22
27
|
}
|
|
@@ -28,6 +28,7 @@ import 'ResultHeadersUtility.dart' as u;
|
|
|
28
28
|
import 'TransformRequestUtility.dart' as u;
|
|
29
29
|
import 'TransformResponseUtility.dart' as u;
|
|
30
30
|
|
|
31
|
+
import 'GraphqlUtility.dart' as u;
|
|
31
32
|
import 'StructUtility.dart';
|
|
32
33
|
|
|
33
34
|
class Utility {
|
|
@@ -55,6 +56,8 @@ class Utility {
|
|
|
55
56
|
dynamic prepareParams = u.prepareParams;
|
|
56
57
|
dynamic preparePath = u.preparePath;
|
|
57
58
|
dynamic prepareQuery = u.prepareQuery;
|
|
59
|
+
dynamic graphqlBody = u.graphqlBody;
|
|
60
|
+
dynamic graphqlErrors = u.graphqlErrors;
|
|
58
61
|
dynamic resultBasic = u.resultBasic;
|
|
59
62
|
dynamic resultBody = u.resultBody;
|
|
60
63
|
dynamic resultHeaders = u.resultHeaders;
|
|
@@ -139,6 +142,12 @@ class Utility {
|
|
|
139
142
|
case 'preparePath':
|
|
140
143
|
preparePath = fn;
|
|
141
144
|
break;
|
|
145
|
+
case 'graphqlBody':
|
|
146
|
+
graphqlBody = fn;
|
|
147
|
+
break;
|
|
148
|
+
case 'graphqlErrors':
|
|
149
|
+
graphqlErrors = fn;
|
|
150
|
+
break;
|
|
142
151
|
case 'prepareQuery':
|
|
143
152
|
prepareQuery = fn;
|
|
144
153
|
break;
|
|
@@ -55,24 +55,93 @@ defmodule ProjectName.Feature.Paging do
|
|
|
55
55
|
paging = if ctrl != nil, do: S.getprop(ctrl, "paging"), else: nil
|
|
56
56
|
paging = if S.ismap(paging), do: paging, else: S.jm([])
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
# GraphQL paginates through operation VARIABLES, not the query
|
|
59
|
+
# string. This hook runs after make_spec, so spec.body already holds
|
|
60
|
+
# the { query, variables } envelope, and before make_fetch_def
|
|
61
|
+
# serialises it.
|
|
62
|
+
if "graphql" == S.getprop(S.getprop(ctx, "point"), "kind") do
|
|
63
|
+
graphql_pre_request(f, ctx, paging)
|
|
64
|
+
else
|
|
65
|
+
cond do
|
|
66
|
+
S.getprop(paging, "cursor") != nil ->
|
|
67
|
+
S.setprop(query, cursor_param, S.getprop(paging, "cursor"))
|
|
68
|
+
|
|
69
|
+
S.getprop(query, page_param) == nil ->
|
|
70
|
+
page = S.getprop(paging, "page")
|
|
71
|
+
page = if page == nil, do: por(S.getprop(opts, "startPage"), 1), else: page
|
|
72
|
+
S.setprop(query, page_param, page)
|
|
73
|
+
|
|
74
|
+
true ->
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
61
77
|
|
|
62
|
-
S.getprop(
|
|
63
|
-
page = S.getprop(paging, "page")
|
|
64
|
-
page = if page == nil, do: por(S.getprop(opts, "startPage"), 1), else: page
|
|
65
|
-
S.setprop(query, page_param, page)
|
|
78
|
+
limit = S.getprop(opts, "limit")
|
|
66
79
|
|
|
67
|
-
|
|
68
|
-
|
|
80
|
+
if limit != nil and S.getprop(query, limit_param) == nil do
|
|
81
|
+
S.setprop(query, limit_param, limit)
|
|
82
|
+
end
|
|
69
83
|
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
70
86
|
|
|
71
|
-
|
|
87
|
+
nil
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Relay pagination: the cursor is the `after` variable (or whatever the model
|
|
91
|
+
# named it), and the page size is `first`.
|
|
92
|
+
defp graphql_pre_request(f, ctx, paging) do
|
|
93
|
+
opts = F.opts(f)
|
|
94
|
+
point = S.getprop(ctx, "point")
|
|
95
|
+
body = S.getprop(S.getprop(ctx, "spec"), "body")
|
|
96
|
+
|
|
97
|
+
if S.ismap(body) do
|
|
98
|
+
variables =
|
|
99
|
+
case S.getprop(body, "variables") do
|
|
100
|
+
v when not is_nil(v) ->
|
|
101
|
+
if S.ismap(v) do
|
|
102
|
+
v
|
|
103
|
+
else
|
|
104
|
+
nv = S.jm([])
|
|
105
|
+
S.setprop(body, "variables", nv)
|
|
106
|
+
nv
|
|
107
|
+
end
|
|
72
108
|
|
|
73
|
-
|
|
74
|
-
|
|
109
|
+
_ ->
|
|
110
|
+
nv = S.jm([])
|
|
111
|
+
S.setprop(body, "variables", nv)
|
|
112
|
+
nv
|
|
75
113
|
end
|
|
114
|
+
|
|
115
|
+
after_var = por(S.getprop(opts, "afterVar"), "after")
|
|
116
|
+
first_var = por(S.getprop(opts, "firstVar"), "first")
|
|
117
|
+
|
|
118
|
+
# Only bind variables the operation actually declares, or the server
|
|
119
|
+
# rejects the document.
|
|
120
|
+
varlist = S.getpath(point, "graphql.vars")
|
|
121
|
+
|
|
122
|
+
declared =
|
|
123
|
+
if S.islist(varlist) and S.size(varlist) > 0 do
|
|
124
|
+
Enum.reduce(0..(S.size(varlist) - 1), MapSet.new(), fn i, acc ->
|
|
125
|
+
case S.getprop(S.getelem(varlist, i), "name") do
|
|
126
|
+
nil -> acc
|
|
127
|
+
name -> MapSet.put(acc, name)
|
|
128
|
+
end
|
|
129
|
+
end)
|
|
130
|
+
else
|
|
131
|
+
MapSet.new()
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
cursor = S.getprop(paging, "cursor")
|
|
135
|
+
|
|
136
|
+
if cursor != nil and MapSet.member?(declared, after_var) do
|
|
137
|
+
S.setprop(variables, after_var, cursor)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
limit = S.getprop(opts, "limit")
|
|
141
|
+
|
|
142
|
+
if limit != nil and S.getprop(variables, first_var) == nil and
|
|
143
|
+
MapSet.member?(declared, first_var) do
|
|
144
|
+
S.setprop(variables, first_var, limit)
|
|
76
145
|
end
|
|
77
146
|
end
|
|
78
147
|
|
|
@@ -131,8 +200,62 @@ defmodule ProjectName.Feature.Paging do
|
|
|
131
200
|
{next_from_link, nil, false}
|
|
132
201
|
end
|
|
133
202
|
|
|
203
|
+
# Relay connections carry the cursor in pageInfo, at the path the
|
|
204
|
+
# model recorded for this op. `explicit_more` is set when the server
|
|
205
|
+
# states hasMore outright, rather than leaving it to be inferred from
|
|
206
|
+
# the presence of a cursor.
|
|
207
|
+
gqlpage = S.getpath(S.getprop(ctx, "point"), "graphql.page")
|
|
208
|
+
|
|
209
|
+
{cursor_val, has_more, explicit_more} =
|
|
210
|
+
if S.ismap(gqlpage) and S.ismap(body) do
|
|
211
|
+
# `connpath` locates the connection object inside the response
|
|
212
|
+
# envelope (data.<field>); the cursor/more paths are relative
|
|
213
|
+
# to it.
|
|
214
|
+
connpath = S.getprop(gqlpage, "connpath")
|
|
215
|
+
|
|
216
|
+
conn =
|
|
217
|
+
if is_binary(connpath) and connpath != "" do
|
|
218
|
+
por(S.getpath(body, connpath), body)
|
|
219
|
+
else
|
|
220
|
+
body
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
cursorpath = S.getprop(gqlpage, "cursor")
|
|
224
|
+
|
|
225
|
+
c =
|
|
226
|
+
if is_binary(cursorpath) and cursorpath != "" do
|
|
227
|
+
por(S.getpath(conn, cursorpath), cursor_val)
|
|
228
|
+
else
|
|
229
|
+
cursor_val
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
morepath = S.getprop(gqlpage, "more")
|
|
233
|
+
|
|
234
|
+
case (is_binary(morepath) and morepath != "" and S.getpath(conn, morepath)) do
|
|
235
|
+
v when is_boolean(v) ->
|
|
236
|
+
{c, v, true}
|
|
237
|
+
|
|
238
|
+
_ ->
|
|
239
|
+
# No usable relay `more` path, but a top-level body hasMore is
|
|
240
|
+
# still an outright statement — keep it explicit, or cursor
|
|
241
|
+
# inference below would flip an explicit false to true.
|
|
242
|
+
{c, has_more, is_boolean(S.getprop(body, "hasMore"))}
|
|
243
|
+
end
|
|
244
|
+
else
|
|
245
|
+
{cursor_val, has_more, is_boolean(S.getprop(body, "hasMore"))}
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Cursor presence only INFERS another page. When the server stated the
|
|
249
|
+
# answer outright — relay's `hasNextPage: false`, or a body `hasMore`
|
|
250
|
+
# — that wins: a final page normally carries both an end cursor and
|
|
251
|
+
# hasNextPage false, and inferring from the cursor there would send
|
|
252
|
+
# the caller back for a page that does not exist, forever.
|
|
134
253
|
has_more =
|
|
135
|
-
|
|
254
|
+
if explicit_more do
|
|
255
|
+
has_more
|
|
256
|
+
else
|
|
257
|
+
has_more or next_val != nil or cursor_val != nil or next_page != nil
|
|
258
|
+
end
|
|
136
259
|
|
|
137
260
|
paging =
|
|
138
261
|
S.jm([
|
|
@@ -13,6 +13,9 @@ defmodule ProjectName.Utility do
|
|
|
13
13
|
|
|
14
14
|
@default_user_agent "Mozilla/5.0 (compatible; ProjectNameSDK/1.0)"
|
|
15
15
|
|
|
16
|
+
# Content type every GraphQL-over-HTTP request uses.
|
|
17
|
+
@graphql_content_type "application/json"
|
|
18
|
+
|
|
16
19
|
# ---- construction / registration ----------------------------------------
|
|
17
20
|
|
|
18
21
|
def new do
|
|
@@ -45,6 +48,8 @@ defmodule ProjectName.Utility do
|
|
|
45
48
|
{"prepare_params", &prepare_params_impl/1},
|
|
46
49
|
{"prepare_path", &prepare_path_impl/1},
|
|
47
50
|
{"prepare_query", &prepare_query_impl/1},
|
|
51
|
+
{"graphql_body", &graphql_body_impl/1},
|
|
52
|
+
{"graphql_errors", &graphql_errors_impl/1},
|
|
48
53
|
{"result_basic", &result_basic_impl/1},
|
|
49
54
|
{"result_body", &result_body_impl/1},
|
|
50
55
|
{"result_headers", &result_headers_impl/1},
|
|
@@ -83,6 +88,8 @@ defmodule ProjectName.Utility do
|
|
|
83
88
|
def prepare_params(ctx), do: u(ctx, "prepare_params").(ctx)
|
|
84
89
|
def prepare_path(ctx), do: u(ctx, "prepare_path").(ctx)
|
|
85
90
|
def prepare_query(ctx), do: u(ctx, "prepare_query").(ctx)
|
|
91
|
+
def graphql_body(ctx), do: u(ctx, "graphql_body").(ctx)
|
|
92
|
+
def graphql_errors(ctx), do: u(ctx, "graphql_errors").(ctx)
|
|
86
93
|
def result_basic(ctx), do: u(ctx, "result_basic").(ctx)
|
|
87
94
|
def result_body(ctx), do: u(ctx, "result_body").(ctx)
|
|
88
95
|
def result_headers(ctx), do: u(ctx, "result_headers").(ctx)
|
|
@@ -249,7 +256,7 @@ defmodule ProjectName.Utility do
|
|
|
249
256
|
"headers" => %{"`$CHILD`" => "`$STRING`"},
|
|
250
257
|
"allow" => %{
|
|
251
258
|
"method" => "GET,PUT,POST,PATCH,DELETE,OPTIONS",
|
|
252
|
-
"op" => "create,update,load,list,remove,command,direct"
|
|
259
|
+
"op" => "create,update,load,list,remove,command,direct,graphql"
|
|
253
260
|
},
|
|
254
261
|
"entity" => %{"`$CHILD`" => %{"`$OPEN`" => true, "active" => false, "alias" => %{}}},
|
|
255
262
|
"feature" => %{"`$CHILD`" => %{"`$OPEN`" => true, "active" => false}},
|
|
@@ -474,8 +481,23 @@ defmodule ProjectName.Utility do
|
|
|
474
481
|
S.setprop(spec, "params", prepare_params(ctx))
|
|
475
482
|
S.setprop(spec, "query", prepare_query(ctx))
|
|
476
483
|
S.setprop(spec, "headers", prepare_headers(ctx))
|
|
477
|
-
|
|
478
|
-
S.
|
|
484
|
+
|
|
485
|
+
if S.getprop(point, "kind") == "graphql" do
|
|
486
|
+
# GraphQL addresses one endpoint: no path parts, no query string,
|
|
487
|
+
# and the body carries the operation. prepare_body is skipped
|
|
488
|
+
# deliberately — it only emits a body for data-input ops, whereas
|
|
489
|
+
# every GraphQL op posts one, including load/list/remove.
|
|
490
|
+
S.setprop(spec, "body", graphql_body(ctx))
|
|
491
|
+
S.setprop(spec, "path", "")
|
|
492
|
+
# prepare_query already copied the op's match arguments into the
|
|
493
|
+
# query string. Those same values are bound as operation
|
|
494
|
+
# variables, so leaving them would send /graphql?id=i1.
|
|
495
|
+
S.setprop(spec, "query", S.jm([]))
|
|
496
|
+
S.setprop(S.getprop(spec, "headers"), "content-type", @graphql_content_type)
|
|
497
|
+
else
|
|
498
|
+
S.setprop(spec, "body", prepare_body(ctx))
|
|
499
|
+
S.setprop(spec, "path", prepare_path(ctx))
|
|
500
|
+
end
|
|
479
501
|
|
|
480
502
|
ctrl = S.getprop(ctx, "ctrl")
|
|
481
503
|
explain = S.getprop(ctrl, "explain")
|
|
@@ -580,6 +602,13 @@ defmodule ProjectName.Utility do
|
|
|
580
602
|
result_basic(ctx)
|
|
581
603
|
result_headers(ctx)
|
|
582
604
|
result_body(ctx)
|
|
605
|
+
|
|
606
|
+
# GraphQL reports failures as a top-level `errors` array under HTTP
|
|
607
|
+
# 200, so result_basic's status check never sees them. Lift them
|
|
608
|
+
# here, before the response transform tries to unwrap data that is
|
|
609
|
+
# not there.
|
|
610
|
+
graphql_errors(ctx)
|
|
611
|
+
|
|
583
612
|
transform_response(ctx)
|
|
584
613
|
|
|
585
614
|
if S.getprop(result, "err") == nil, do: S.setprop(result, "ok", true)
|
|
@@ -994,6 +1023,161 @@ defmodule ProjectName.Utility do
|
|
|
994
1023
|
out
|
|
995
1024
|
end
|
|
996
1025
|
|
|
1026
|
+
# ---- graphql -------------------------------------------------------------
|
|
1027
|
+
#
|
|
1028
|
+
# GraphQL transport. API-INDEPENDENT: every GraphQL SDK this generator
|
|
1029
|
+
# produces uses this code unchanged. The API-specific part — which
|
|
1030
|
+
# operations exist and what each one's document is — is model data,
|
|
1031
|
+
# computed once by apidef and emitted into Config.
|
|
1032
|
+
#
|
|
1033
|
+
# Two jobs:
|
|
1034
|
+
#
|
|
1035
|
+
# graphql_body — build %{query, variables} for a point, binding the
|
|
1036
|
+
# op's arguments to the document's declared variables.
|
|
1037
|
+
#
|
|
1038
|
+
# graphql_errors — lift a GraphQL failure into an SDK error. GraphQL
|
|
1039
|
+
# reports failures as a top-level `errors` array under
|
|
1040
|
+
# HTTP 200, so the status-driven path in result_basic
|
|
1041
|
+
# never sees them.
|
|
1042
|
+
|
|
1043
|
+
# Map a GraphQL error to the same error codes the HTTP path produces, so a
|
|
1044
|
+
# caller handles auth or rate limiting identically on both transports.
|
|
1045
|
+
# Servers put the machine-readable code in `extensions.code`; Linear-style
|
|
1046
|
+
# APIs use `extensions.type`.
|
|
1047
|
+
def graphql_error_code(gqlerr) do
|
|
1048
|
+
ext = S.getprop(gqlerr, "extensions")
|
|
1049
|
+
|
|
1050
|
+
code = strv(S.getprop(ext, "code"))
|
|
1051
|
+
code = if code == "", do: strv(S.getprop(ext, "type")), else: code
|
|
1052
|
+
raw = String.upcase(code)
|
|
1053
|
+
|
|
1054
|
+
cond do
|
|
1055
|
+
String.contains?(raw, "AUTH") or String.contains?(raw, "FORBIDDEN") or
|
|
1056
|
+
String.contains?(raw, "UNAUTHENTICATED") ->
|
|
1057
|
+
"request_auth"
|
|
1058
|
+
|
|
1059
|
+
String.contains?(raw, "RATELIMIT") or String.contains?(raw, "RATE_LIMIT") or
|
|
1060
|
+
String.contains?(raw, "TOO_MANY") ->
|
|
1061
|
+
"request_ratelimit"
|
|
1062
|
+
|
|
1063
|
+
String.contains?(raw, "BAD_USER_INPUT") or String.contains?(raw, "VALIDATION") or
|
|
1064
|
+
String.contains?(raw, "INVALID") ->
|
|
1065
|
+
"request_invalid"
|
|
1066
|
+
|
|
1067
|
+
true ->
|
|
1068
|
+
"request_graphql"
|
|
1069
|
+
end
|
|
1070
|
+
end
|
|
1071
|
+
|
|
1072
|
+
# Build the request body for a GraphQL point.
|
|
1073
|
+
#
|
|
1074
|
+
# Variables come from the op's own arguments: a named variable binds to the
|
|
1075
|
+
# like-named argument (`from`), and the input-object variable (empty
|
|
1076
|
+
# `from`) takes the request data as a whole — which is what makes a
|
|
1077
|
+
# generated create/update call look exactly like its REST equivalent.
|
|
1078
|
+
def graphql_body_impl(ctx) do
|
|
1079
|
+
gql = S.getprop(S.getprop(ctx, "point"), "graphql")
|
|
1080
|
+
|
|
1081
|
+
if not S.ismap(gql) do
|
|
1082
|
+
nil
|
|
1083
|
+
else
|
|
1084
|
+
# reqmatch/reqdata hold the caller's arguments for THIS call; data/match
|
|
1085
|
+
# hold the entity's current state. Which pair depends on whether the op
|
|
1086
|
+
# takes match or data input. A named variable falls back to the current
|
|
1087
|
+
# state, so updating a loaded entity with just {title} still binds the
|
|
1088
|
+
# stored id the mutation requires.
|
|
1089
|
+
op = S.getprop(ctx, "op")
|
|
1090
|
+
datainput = S.getprop(op, "input") == "data"
|
|
1091
|
+
|
|
1092
|
+
reqsrc =
|
|
1093
|
+
if datainput, do: S.getprop(ctx, "reqdata"), else: S.getprop(ctx, "reqmatch")
|
|
1094
|
+
|
|
1095
|
+
datasrc =
|
|
1096
|
+
if datainput, do: S.getprop(ctx, "data"), else: S.getprop(ctx, "match")
|
|
1097
|
+
|
|
1098
|
+
reqsrc = if S.ismap(reqsrc), do: reqsrc, else: S.jm([])
|
|
1099
|
+
datasrc = if S.ismap(datasrc), do: datasrc, else: S.jm([])
|
|
1100
|
+
|
|
1101
|
+
variables = S.jm([])
|
|
1102
|
+
varlist = S.getprop(gql, "vars")
|
|
1103
|
+
n = if S.islist(varlist), do: S.size(varlist), else: 0
|
|
1104
|
+
|
|
1105
|
+
if n > 0 do
|
|
1106
|
+
Enum.each(0..(n - 1), fn i ->
|
|
1107
|
+
spec = S.getelem(varlist, i)
|
|
1108
|
+
|
|
1109
|
+
if S.ismap(spec) do
|
|
1110
|
+
name = strv(S.getprop(spec, "name"))
|
|
1111
|
+
from = strv(S.getprop(spec, "from"))
|
|
1112
|
+
|
|
1113
|
+
cond do
|
|
1114
|
+
name == "" ->
|
|
1115
|
+
nil
|
|
1116
|
+
|
|
1117
|
+
from == "" ->
|
|
1118
|
+
# The input object IS the request body. Strip the action
|
|
1119
|
+
# selector, which is an SDK-side point discriminator, not an
|
|
1120
|
+
# API field.
|
|
1121
|
+
body = S.jm([])
|
|
1122
|
+
|
|
1123
|
+
Enum.each(H.entries(reqsrc), fn {key, val} ->
|
|
1124
|
+
if key != "$action", do: S.setprop(body, key, val)
|
|
1125
|
+
end)
|
|
1126
|
+
|
|
1127
|
+
S.setprop(variables, name, body)
|
|
1128
|
+
|
|
1129
|
+
true ->
|
|
1130
|
+
# Only send variables the caller actually supplied: sending
|
|
1131
|
+
# an explicit null would clear a field on many APIs.
|
|
1132
|
+
# Explicit nil check, not `||`: a caller-supplied `false` is a
|
|
1133
|
+
# value, and `||` would treat it as absent and substitute the
|
|
1134
|
+
# current state — sending the opposite of what was asked.
|
|
1135
|
+
val = S.getprop(reqsrc, from)
|
|
1136
|
+
val = if val == nil, do: S.getprop(datasrc, from), else: val
|
|
1137
|
+
if val != nil, do: S.setprop(variables, name, val)
|
|
1138
|
+
end
|
|
1139
|
+
end
|
|
1140
|
+
end)
|
|
1141
|
+
end
|
|
1142
|
+
|
|
1143
|
+
S.jm(["query", S.getprop(gql, "doc"), "variables", variables])
|
|
1144
|
+
end
|
|
1145
|
+
end
|
|
1146
|
+
|
|
1147
|
+
# Inspect a decoded GraphQL response body and record a failure when the
|
|
1148
|
+
# server reported one. Returns true when an error was recorded.
|
|
1149
|
+
#
|
|
1150
|
+
# Partial data (`data` alongside `errors`) is treated as failure: the REST
|
|
1151
|
+
# surface has no partial-success concept, and silently returning half an
|
|
1152
|
+
# object would be worse than failing.
|
|
1153
|
+
def graphql_errors_impl(ctx) do
|
|
1154
|
+
result = S.getprop(ctx, "result")
|
|
1155
|
+
point = S.getprop(ctx, "point")
|
|
1156
|
+
|
|
1157
|
+
errors = if result == nil, do: nil, else: S.getprop(S.getprop(result, "body"), "errors")
|
|
1158
|
+
|
|
1159
|
+
count = if S.islist(errors), do: S.size(errors), else: 0
|
|
1160
|
+
|
|
1161
|
+
if result == nil or S.getprop(point, "kind") != "graphql" or count == 0 do
|
|
1162
|
+
false
|
|
1163
|
+
else
|
|
1164
|
+
first = S.getelem(errors, 0)
|
|
1165
|
+
msg = strv(S.getprop(first, "message"))
|
|
1166
|
+
msg = if msg == "", do: "graphql error", else: msg
|
|
1167
|
+
msg = if 1 < count, do: msg <> " (+" <> Integer.to_string(count - 1) <> " more)", else: msg
|
|
1168
|
+
|
|
1169
|
+
S.setprop(
|
|
1170
|
+
result,
|
|
1171
|
+
"err",
|
|
1172
|
+
Context.make_error(ctx, graphql_error_code(first), "graphql: " <> msg)
|
|
1173
|
+
)
|
|
1174
|
+
|
|
1175
|
+
S.setprop(result, "ok", false)
|
|
1176
|
+
|
|
1177
|
+
true
|
|
1178
|
+
end
|
|
1179
|
+
end
|
|
1180
|
+
|
|
997
1181
|
def prepare_auth_impl(ctx) do
|
|
998
1182
|
spec = S.getprop(ctx, "spec")
|
|
999
1183
|
|
|
@@ -25,6 +25,8 @@ type Utility struct {
|
|
|
25
25
|
PrepareParams func(ctx *Context) map[string]any
|
|
26
26
|
PreparePath func(ctx *Context) string
|
|
27
27
|
PrepareQuery func(ctx *Context) map[string]any
|
|
28
|
+
GraphqlBody func(ctx *Context) any
|
|
29
|
+
GraphqlErrors func(ctx *Context) bool
|
|
28
30
|
ResultBasic func(ctx *Context) *Result
|
|
29
31
|
ResultBody func(ctx *Context) *Result
|
|
30
32
|
ResultHeaders func(ctx *Context) *Result
|