@voxgig/sdkgen 1.5.1 → 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.
Files changed (270) hide show
  1. package/dist/cmp/AgentGuideContent.js +5 -0
  2. package/dist/cmp/AgentGuideContent.js.map +1 -1
  3. package/dist/cmp/Feature.js +13 -0
  4. package/dist/cmp/Feature.js.map +1 -1
  5. package/dist/helpers/canonType.d.ts +3 -1
  6. package/dist/helpers/canonType.js +44 -1
  7. package/dist/helpers/canonType.js.map +1 -1
  8. package/dist/helpers/packageMeta.js +7 -0
  9. package/dist/helpers/packageMeta.js.map +1 -1
  10. package/dist/sdkgen.d.ts +3 -3
  11. package/dist/sdkgen.js +4 -1
  12. package/dist/sdkgen.js.map +1 -1
  13. package/dist/tsconfig.tsbuildinfo +1 -1
  14. package/package.json +3 -3
  15. package/project/.sdk/model/feature/paging.aontu +7 -0
  16. package/project/.sdk/model/target/py-data.aontu +64 -0
  17. package/project/.sdk/src/cmp/c/fragment/Main.fragment.c +89 -1
  18. package/project/.sdk/src/cmp/clojure/Main_clojure.ts +1 -0
  19. package/project/.sdk/src/cmp/csharp/fragment/Main.fragment.cs +97 -0
  20. package/project/.sdk/src/cmp/dart/fragment/Main.fragment.dart +78 -0
  21. package/project/.sdk/src/cmp/elixir/fragment/Main.fragment.ex +84 -0
  22. package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +87 -0
  23. package/project/.sdk/src/cmp/js/fragment/Main.fragment.js +75 -0
  24. package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +85 -0
  25. package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +4 -0
  26. package/project/.sdk/src/cmp/perl/fragment/Main.fragment.pm +80 -0
  27. package/project/.sdk/src/cmp/php/fragment/Main.fragment.php +86 -0
  28. package/project/.sdk/src/cmp/py/Entity_py.ts +9 -2
  29. package/project/.sdk/src/cmp/py/MainEntity_py.ts +1 -1
  30. package/project/.sdk/src/cmp/py/Main_py.ts +34 -6
  31. package/project/.sdk/src/cmp/py/Package_py.ts +6 -9
  32. package/project/.sdk/src/cmp/py/TestDirect_py.ts +2 -2
  33. package/project/.sdk/src/cmp/py/TestEntity_py.ts +3 -3
  34. package/project/.sdk/src/cmp/py/fragment/Entity.fragment.py +2 -2
  35. package/project/.sdk/src/cmp/py/fragment/EntityCreateOp.fragment.py +2 -2
  36. package/project/.sdk/src/cmp/py/fragment/EntityLoadOp.fragment.py +2 -2
  37. package/project/.sdk/src/cmp/py/fragment/EntityRemoveOp.fragment.py +2 -2
  38. package/project/.sdk/src/cmp/py/fragment/EntityUpdateOp.fragment.py +2 -2
  39. package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +75 -8
  40. package/project/.sdk/src/cmp/py-data/Main_py-data.ts +987 -0
  41. package/project/.sdk/src/cmp/rb/fragment/Main.fragment.rb +71 -0
  42. package/project/.sdk/src/cmp/rust/fragment/Main.fragment.rs +95 -0
  43. package/project/.sdk/src/cmp/swift/fragment/Main.fragment.swift +86 -0
  44. package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +75 -0
  45. package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +95 -0
  46. package/project/.sdk/tm/c/core/sdk.h +7 -0
  47. package/project/.sdk/tm/c/feature/paging.c +141 -2
  48. package/project/.sdk/tm/c/utility/graphql.c +178 -0
  49. package/project/.sdk/tm/c/utility/make_options.c +1 -1
  50. package/project/.sdk/tm/c/utility/make_response.c +6 -0
  51. package/project/.sdk/tm/c/utility/make_spec.c +23 -3
  52. package/project/.sdk/tm/clojure/src/sdk/client.clj +64 -1
  53. package/project/.sdk/tm/clojure/src/sdk/core.clj +126 -3
  54. package/project/.sdk/tm/clojure/src/sdk/features.clj +80 -14
  55. package/project/.sdk/tm/cpp/core/types.hpp +83 -0
  56. package/project/.sdk/tm/cpp/feature/paging.hpp +103 -1
  57. package/project/.sdk/tm/cpp/utility/pipeline.hpp +152 -3
  58. package/project/.sdk/tm/csharp/core/Utility.cs +4 -0
  59. package/project/.sdk/tm/csharp/feature/PagingFeature.cs +118 -1
  60. package/project/.sdk/tm/csharp/utility/Graphql.cs +190 -0
  61. package/project/.sdk/tm/csharp/utility/MakeOptions.cs +1 -1
  62. package/project/.sdk/tm/csharp/utility/MakeResponse.cs +7 -0
  63. package/project/.sdk/tm/csharp/utility/MakeSpec.cs +21 -2
  64. package/project/.sdk/tm/csharp/utility/Register.cs +2 -0
  65. package/project/.sdk/tm/dart/lib/Point.dart +7 -0
  66. package/project/.sdk/tm/dart/lib/feature/paging/PagingFeature.dart +99 -4
  67. package/project/.sdk/tm/dart/lib/utility/GraphqlUtility.dart +162 -0
  68. package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +1 -1
  69. package/project/.sdk/tm/dart/lib/utility/MakeResponseUtility.dart +5 -0
  70. package/project/.sdk/tm/dart/lib/utility/MakeSpecUtility.dart +18 -2
  71. package/project/.sdk/tm/dart/lib/utility/PrepareMethodUtility.dart +6 -1
  72. package/project/.sdk/tm/dart/lib/utility/Utility.dart +9 -0
  73. package/project/.sdk/tm/elixir/lib/projectname/feature/paging.ex +136 -13
  74. package/project/.sdk/tm/elixir/lib/projectname/utility.ex +187 -3
  75. package/project/.sdk/tm/go/core/utility_type.go +2 -0
  76. package/project/.sdk/tm/go/feature/paging_feature.go +87 -1
  77. package/project/.sdk/tm/go/utility/graphql.go +168 -0
  78. package/project/.sdk/tm/go/utility/make_options.go +1 -1
  79. package/project/.sdk/tm/go/utility/make_response.go +6 -0
  80. package/project/.sdk/tm/go/utility/make_spec.go +19 -2
  81. package/project/.sdk/tm/go/utility/register.go +2 -0
  82. package/project/.sdk/tm/haskell/src/SdkFeatures.hs +146 -7
  83. package/project/.sdk/tm/haskell/src/SdkRuntime.hs +153 -3
  84. package/project/.sdk/tm/java/core/SdkClient.java +93 -0
  85. package/project/.sdk/tm/java/core/Utility.java +4 -0
  86. package/project/.sdk/tm/java/feature/PagingFeature.java +100 -1
  87. package/project/.sdk/tm/java/utility/Graphql.java +191 -0
  88. package/project/.sdk/tm/java/utility/MakeOptions.java +1 -1
  89. package/project/.sdk/tm/java/utility/MakeResponse.java +6 -0
  90. package/project/.sdk/tm/java/utility/MakeSpec.java +17 -2
  91. package/project/.sdk/tm/java/utility/Register.java +2 -0
  92. package/project/.sdk/tm/js/src/Point.js +5 -0
  93. package/project/.sdk/tm/js/src/feature/paging/PagingFeature.js +82 -3
  94. package/project/.sdk/tm/js/src/feature/test/TestFeature.js +14 -2
  95. package/project/.sdk/tm/js/src/utility/GraphqlUtility.js +148 -0
  96. package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +1 -1
  97. package/project/.sdk/tm/js/src/utility/MakeResponseUtility.js +7 -0
  98. package/project/.sdk/tm/js/src/utility/MakeSpecUtility.js +19 -2
  99. package/project/.sdk/tm/js/src/utility/Utility.js +4 -0
  100. package/project/.sdk/tm/kotlin/core/SdkClient.kt +84 -0
  101. package/project/.sdk/tm/kotlin/core/Utility.kt +2 -0
  102. package/project/.sdk/tm/kotlin/feature/PagingFeature.kt +92 -1
  103. package/project/.sdk/tm/kotlin/utility/Graphql.kt +145 -0
  104. package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +1 -1
  105. package/project/.sdk/tm/kotlin/utility/MakeReqRes.kt +6 -0
  106. package/project/.sdk/tm/kotlin/utility/MakeSpecUrl.kt +17 -2
  107. package/project/.sdk/tm/kotlin/utility/Register.kt +2 -0
  108. package/project/.sdk/tm/lean/src/SdkUtility.lean +134 -1
  109. package/project/.sdk/tm/lua/feature/paging_feature.lua +97 -2
  110. package/project/.sdk/tm/lua/utility/graphql.lua +163 -0
  111. package/project/.sdk/tm/lua/utility/make_options.lua +1 -1
  112. package/project/.sdk/tm/lua/utility/make_response.lua +6 -0
  113. package/project/.sdk/tm/lua/utility/make_spec.lua +18 -2
  114. package/project/.sdk/tm/lua/utility/register.lua +3 -0
  115. package/project/.sdk/tm/ocaml/sdk_features.ml +164 -20
  116. package/project/.sdk/tm/ocaml/sdk_runtime.ml +143 -4
  117. package/project/.sdk/tm/ocaml/sdk_types.ml +2 -0
  118. package/project/.sdk/tm/perl/feature/paging_feature.pm +91 -3
  119. package/project/.sdk/tm/perl/utility/graphql.pm +150 -0
  120. package/project/.sdk/tm/perl/utility/make_options.pm +1 -1
  121. package/project/.sdk/tm/perl/utility/make_response.pm +6 -0
  122. package/project/.sdk/tm/perl/utility/make_spec.pm +20 -2
  123. package/project/.sdk/tm/perl/utility/register.pm +1 -0
  124. package/project/.sdk/tm/php/core/UtilityType.php +2 -0
  125. package/project/.sdk/tm/php/feature/PagingFeature.php +106 -4
  126. package/project/.sdk/tm/php/utility/Graphql.php +176 -0
  127. package/project/.sdk/tm/php/utility/MakeOptions.php +1 -1
  128. package/project/.sdk/tm/php/utility/MakeResponse.php +7 -0
  129. package/project/.sdk/tm/php/utility/MakeSpec.php +20 -2
  130. package/project/.sdk/tm/php/utility/Register.php +3 -0
  131. package/project/.sdk/tm/py/pkg/core/__init__.py +0 -0
  132. package/project/.sdk/tm/py/{core → pkg/core}/context.py +8 -8
  133. package/project/.sdk/tm/py/{core → pkg/core}/operation.py +1 -1
  134. package/project/.sdk/tm/py/{core → pkg/core}/response.py +1 -1
  135. package/project/.sdk/tm/py/{core → pkg/core}/result.py +1 -1
  136. package/project/.sdk/tm/py/{core → pkg/core}/utility_type.py +2 -0
  137. package/project/.sdk/tm/py/pkg/entity/__init__.py +0 -0
  138. package/project/.sdk/tm/py/pkg/feature/__init__.py +0 -0
  139. package/project/.sdk/tm/py/{feature → pkg/feature}/audit_feature.py +1 -1
  140. package/project/.sdk/tm/py/{feature → pkg/feature}/cache_feature.py +1 -1
  141. package/project/.sdk/tm/py/{feature → pkg/feature}/clienttrack_feature.py +1 -1
  142. package/project/.sdk/tm/py/{feature → pkg/feature}/debug_feature.py +1 -1
  143. package/project/.sdk/tm/py/{feature → pkg/feature}/idempotency_feature.py +1 -1
  144. package/project/.sdk/tm/py/{feature → pkg/feature}/log_feature.py +1 -1
  145. package/project/.sdk/tm/py/{feature → pkg/feature}/metrics_feature.py +1 -1
  146. package/project/.sdk/tm/py/{feature → pkg/feature}/netsim_feature.py +1 -1
  147. package/project/.sdk/tm/py/{feature → pkg/feature}/paging_feature.py +87 -6
  148. package/project/.sdk/tm/py/{feature → pkg/feature}/proxy_feature.py +1 -1
  149. package/project/.sdk/tm/py/{feature → pkg/feature}/ratelimit_feature.py +1 -1
  150. package/project/.sdk/tm/py/{feature → pkg/feature}/rbac_feature.py +1 -1
  151. package/project/.sdk/tm/py/{feature → pkg/feature}/retry_feature.py +1 -1
  152. package/project/.sdk/tm/py/{feature → pkg/feature}/streaming_feature.py +1 -1
  153. package/project/.sdk/tm/py/{feature → pkg/feature}/telemetry_feature.py +1 -1
  154. package/project/.sdk/tm/py/{feature → pkg/feature}/test_feature.py +2 -2
  155. package/project/.sdk/tm/py/{feature → pkg/feature}/timeout_feature.py +1 -1
  156. package/project/.sdk/tm/py/pkg/utility/__init__.py +0 -0
  157. package/project/.sdk/tm/py/{utility → pkg/utility}/feature_init.py +1 -1
  158. package/project/.sdk/tm/py/{utility → pkg/utility}/fetcher.py +1 -1
  159. package/project/.sdk/tm/py/pkg/utility/graphql.py +145 -0
  160. package/project/.sdk/tm/py/{utility → pkg/utility}/make_context.py +1 -1
  161. package/project/.sdk/tm/py/{utility → pkg/utility}/make_error.py +5 -5
  162. package/project/.sdk/tm/py/{utility → pkg/utility}/make_fetch_def.py +2 -2
  163. package/project/.sdk/tm/py/{utility → pkg/utility}/make_options.py +2 -2
  164. package/project/.sdk/tm/py/{utility → pkg/utility}/make_point.py +2 -2
  165. package/project/.sdk/tm/py/{utility → pkg/utility}/make_request.py +2 -2
  166. package/project/.sdk/tm/py/{utility → pkg/utility}/make_response.py +6 -0
  167. package/project/.sdk/tm/py/{utility → pkg/utility}/make_spec.py +19 -4
  168. package/project/.sdk/tm/py/{utility → pkg/utility}/make_url.py +1 -1
  169. package/project/.sdk/tm/py/{utility → pkg/utility}/param.py +2 -2
  170. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_auth.py +1 -1
  171. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_headers.py +1 -1
  172. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_method.py +1 -1
  173. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_params.py +1 -1
  174. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_path.py +1 -1
  175. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_query.py +1 -1
  176. package/project/.sdk/tm/py/pkg/utility/register.py +71 -0
  177. package/project/.sdk/tm/py/{utility → pkg/utility}/transform_request.py +2 -2
  178. package/project/.sdk/tm/py/{utility → pkg/utility}/transform_response.py +2 -2
  179. package/project/.sdk/tm/py/test/feature_harness.py +6 -6
  180. package/project/.sdk/tm/py/test/runner.py +1 -1
  181. package/project/.sdk/tm/py/test/struct_runner.py +1 -1
  182. package/project/.sdk/tm/py/test/test_pipeline.py +5 -5
  183. package/project/.sdk/tm/py/test/test_primary_utility.py +8 -8
  184. package/project/.sdk/tm/py/test/test_struct_utility.py +3 -3
  185. package/project/.sdk/tm/py-data/LICENSE +22 -0
  186. package/project/.sdk/tm/py-data/Makefile +57 -0
  187. package/project/.sdk/tm/py-data/pkg/auth.py +131 -0
  188. package/project/.sdk/tm/py-data/pkg/fetch.py +178 -0
  189. package/project/.sdk/tm/py-data/pkg/frames.py +236 -0
  190. package/project/.sdk/tm/py-data/src/feature/README.md +8 -0
  191. package/project/.sdk/tm/py-data/src/feature/debug/.gitkeep +0 -0
  192. package/project/.sdk/tm/py-data/src/feature/idempotency/.gitkeep +0 -0
  193. package/project/.sdk/tm/py-data/src/feature/log/.gitkeep +0 -0
  194. package/project/.sdk/tm/py-data/src/feature/metrics/.gitkeep +0 -0
  195. package/project/.sdk/tm/py-data/src/feature/netsim/.gitkeep +0 -0
  196. package/project/.sdk/tm/py-data/src/feature/paging/.gitkeep +0 -0
  197. package/project/.sdk/tm/py-data/src/feature/proxy/.gitkeep +0 -0
  198. package/project/.sdk/tm/py-data/src/feature/ratelimit/.gitkeep +0 -0
  199. package/project/.sdk/tm/py-data/src/feature/rbac/.gitkeep +0 -0
  200. package/project/.sdk/tm/py-data/src/feature/retry/.gitkeep +0 -0
  201. package/project/.sdk/tm/py-data/src/feature/streaming/.gitkeep +0 -0
  202. package/project/.sdk/tm/py-data/src/feature/telemetry/.gitkeep +0 -0
  203. package/project/.sdk/tm/py-data/src/feature/test/.gitkeep +0 -0
  204. package/project/.sdk/tm/py-data/src/feature/timeout/.gitkeep +0 -0
  205. package/project/.sdk/tm/py-data/test/__init__.py +0 -0
  206. package/project/.sdk/tm/py-data/test/test_auth.py +95 -0
  207. package/project/.sdk/tm/py-data/test/test_fetch.py +152 -0
  208. package/project/.sdk/tm/py-data/test/test_frames.py +250 -0
  209. package/project/.sdk/tm/rb/core/utility_type.rb +1 -0
  210. package/project/.sdk/tm/rb/feature/paging_feature.rb +89 -3
  211. package/project/.sdk/tm/rb/utility/graphql.rb +131 -0
  212. package/project/.sdk/tm/rb/utility/make_options.rb +1 -1
  213. package/project/.sdk/tm/rb/utility/make_response.rb +6 -0
  214. package/project/.sdk/tm/rb/utility/make_spec.rb +18 -2
  215. package/project/.sdk/tm/rb/utility/register.rb +3 -0
  216. package/project/.sdk/tm/rust/feature/paging.rs +124 -2
  217. package/project/.sdk/tm/rust/utility/graphql.rs +193 -0
  218. package/project/.sdk/tm/rust/utility/make_options.rs +1 -1
  219. package/project/.sdk/tm/rust/utility/make_response.rs +6 -0
  220. package/project/.sdk/tm/rust/utility/make_spec.rs +32 -4
  221. package/project/.sdk/tm/rust/utility/mod.rs +1 -0
  222. package/project/.sdk/tm/scala/core/SdkClient.scala +90 -0
  223. package/project/.sdk/tm/scala/core/Utility.scala +4 -0
  224. package/project/.sdk/tm/scala/feature/PagingFeature.scala +104 -3
  225. package/project/.sdk/tm/scala/utility/Graphql.scala +160 -0
  226. package/project/.sdk/tm/scala/utility/Make.scala +7 -1
  227. package/project/.sdk/tm/scala/utility/MakeSpec.scala +22 -2
  228. package/project/.sdk/tm/scala/utility/Register.scala +2 -0
  229. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Utility.swift +4 -0
  230. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/PagingFeature.swift +86 -1
  231. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Graphql.swift +142 -0
  232. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Make.swift +24 -2
  233. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +1 -1
  234. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Register.swift +2 -0
  235. package/project/.sdk/tm/ts/src/Point.ts +7 -0
  236. package/project/.sdk/tm/ts/src/feature/paging/PagingFeature.ts +82 -3
  237. package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +14 -2
  238. package/project/.sdk/tm/ts/src/utility/GraphqlUtility.ts +152 -0
  239. package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +1 -1
  240. package/project/.sdk/tm/ts/src/utility/MakeResponseUtility.ts +7 -0
  241. package/project/.sdk/tm/ts/src/utility/MakeSpecUtility.ts +19 -2
  242. package/project/.sdk/tm/ts/src/utility/Utility.ts +4 -0
  243. package/project/.sdk/tm/zig/core/utility.zig +198 -3
  244. package/project/.sdk/tm/zig/feature/paging.zig +128 -1
  245. package/src/cmp/AgentGuideContent.ts +5 -0
  246. package/src/cmp/Feature.ts +13 -0
  247. package/src/helpers/canonType.ts +46 -0
  248. package/src/helpers/packageMeta.ts +7 -0
  249. package/src/sdkgen.ts +6 -1
  250. package/project/.sdk/tm/py/utility/register.py +0 -68
  251. /package/project/.sdk/tm/py/{core → pkg/core}/control.py +0 -0
  252. /package/project/.sdk/tm/py/{core → pkg/core}/error.py +0 -0
  253. /package/project/.sdk/tm/py/{core → pkg/core}/helpers.py +0 -0
  254. /package/project/.sdk/tm/py/{core → pkg/core}/spec.py +0 -0
  255. /package/project/.sdk/tm/py/{feature → pkg/feature}/base_feature.py +0 -0
  256. /package/project/.sdk/tm/py/{utility → pkg/utility}/clean.py +0 -0
  257. /package/project/.sdk/tm/py/{utility → pkg/utility}/done.py +0 -0
  258. /package/project/.sdk/tm/py/{utility → pkg/utility}/feature_add.py +0 -0
  259. /package/project/.sdk/tm/py/{utility → pkg/utility}/feature_hook.py +0 -0
  260. /package/project/.sdk/tm/py/{utility → pkg/utility}/make_result.py +0 -0
  261. /package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_body.py +0 -0
  262. /package/project/.sdk/tm/py/{utility → pkg/utility}/result_basic.py +0 -0
  263. /package/project/.sdk/tm/py/{utility → pkg/utility}/result_body.py +0 -0
  264. /package/project/.sdk/tm/py/{utility → pkg/utility}/result_headers.py +0 -0
  265. /package/project/.sdk/tm/py/{utility → pkg/utility}/voxgig_struct/__init__.py +0 -0
  266. /package/project/.sdk/tm/py/{utility → pkg/utility}/voxgig_struct/voxgig_struct.py +0 -0
  267. /package/project/.sdk/tm/{py/core/__init__.py → py-data/src/feature/audit/.gitkeep} +0 -0
  268. /package/project/.sdk/tm/{py/entity/__init__.py → py-data/src/feature/base/.gitkeep} +0 -0
  269. /package/project/.sdk/tm/{py/feature/__init__.py → py-data/src/feature/cache/.gitkeep} +0 -0
  270. /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
- ctx.spec.body = prepareBody(ctx);
48
- ctx.spec.path = preparePath(ctx);
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
- final method = vs.getprop(ctx.point, 'method');
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
- cond do
59
- S.getprop(paging, "cursor") != nil ->
60
- S.setprop(query, cursor_param, S.getprop(paging, "cursor"))
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(query, page_param) == nil ->
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
- true ->
68
- nil
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
- limit = S.getprop(opts, "limit")
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
- if limit != nil and S.getprop(query, limit_param) == nil do
74
- S.setprop(query, limit_param, limit)
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
- has_more or next_val != nil or cursor_val != nil or next_page != nil
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
- S.setprop(spec, "body", prepare_body(ctx))
478
- S.setprop(spec, "path", prepare_path(ctx))
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