@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
@@ -1,8 +1,9 @@
1
1
  package SCALAPACKAGE.feature
2
2
 
3
- import java.util.{LinkedHashMap, Map => JMap}
3
+ import java.util.{LinkedHashMap, List => JList, Map => JMap}
4
4
  import java.util.regex.{Matcher, Pattern}
5
5
  import SCALAPACKAGE.core.{Context, SdkClient}
6
+ import SCALAPACKAGE.utility.struct.Struct
6
7
 
7
8
  // Pagination support for list operations. On the way out (PreRequest) it
8
9
  // stamps page/limit (or a cursor) into the request query; on the way back
@@ -52,6 +53,14 @@ class PagingFeature extends BaseFeature("paging", "0.0.1", true) {
52
53
  paging = new LinkedHashMap[String, Object]()
53
54
  }
54
55
 
56
+ // GraphQL paginates through operation VARIABLES, not the query string.
57
+ // This hook runs after makeSpec, so spec.body already holds the
58
+ // { query, variables } envelope, and before makeFetchDef serialises it.
59
+ if ("graphql" == Struct.getprop(ctx.point, "kind")) {
60
+ graphqlPreRequest(ctx, paging)
61
+ return
62
+ }
63
+
55
64
  val cursor = paging.get("cursor")
56
65
  if (cursor != null) {
57
66
  spec.query.put(cursorParam, cursor)
@@ -69,6 +78,52 @@ class PagingFeature extends BaseFeature("paging", "0.0.1", true) {
69
78
  }
70
79
  }
71
80
 
81
+ // Relay pagination: the cursor is the `after` variable (or whatever the
82
+ // model named it), and the page size is `first`.
83
+ private def graphqlPreRequest(ctx: Context, paging: JMap[String, Object]): Unit = {
84
+ val body = ctx.spec.body match {
85
+ case m: JMap[_, _] => m.asInstanceOf[JMap[String, Object]]
86
+ case _ => return
87
+ }
88
+
89
+ val variables = body.get("variables") match {
90
+ case m: JMap[_, _] => m.asInstanceOf[JMap[String, Object]]
91
+ case _ =>
92
+ val nv = new LinkedHashMap[String, Object]()
93
+ body.put("variables", nv)
94
+ nv
95
+ }
96
+
97
+ val afterVar = FeatureOptions.foptStr(this.options, "afterVar", "after")
98
+ val firstVar = FeatureOptions.foptStr(this.options, "firstVar", "first")
99
+
100
+ // Only bind variables the operation actually declares, or the server
101
+ // rejects the document.
102
+ val declared = new java.util.HashSet[String]()
103
+ Struct.getpath(ctx.point, java.util.List.of("graphql", "vars")) match {
104
+ case vl: JList[_] =>
105
+ val it = vl.iterator()
106
+ while (it.hasNext) {
107
+ Struct.getprop(it.next(), "name") match {
108
+ case n: String => declared.add(n)
109
+ case _ =>
110
+ }
111
+ }
112
+ case _ =>
113
+ }
114
+
115
+ val cursor = paging.get("cursor")
116
+ if (cursor != null && declared.contains(afterVar)) {
117
+ variables.put(afterVar, cursor)
118
+ }
119
+
120
+ if (this.options.get("limit") != null && variables.get(firstVar) == null &&
121
+ declared.contains(firstVar)) {
122
+ variables.put(firstVar,
123
+ java.lang.Integer.valueOf(FeatureOptions.foptInt(this.options, "limit", 0)))
124
+ }
125
+ }
126
+
72
127
  override def preResult(ctx: Context): Unit = {
73
128
  if (!this.active || !isList(ctx)) {
74
129
  return
@@ -98,6 +153,45 @@ class PagingFeature extends BaseFeature("paging", "0.0.1", true) {
98
153
  case _ =>
99
154
  }
100
155
 
156
+ // Set when the response states hasMore outright, rather than leaving it
157
+ // to be inferred from the presence of a cursor.
158
+ var explicitMore = false
159
+
160
+ // Relay connections carry the cursor in pageInfo, at the path the model
161
+ // recorded for this op.
162
+ (Struct.getpath(ctx.point, java.util.List.of("graphql", "page")), body) match {
163
+ case (pg: JMap[_, _], _: JMap[_, _]) =>
164
+ val page = pg.asInstanceOf[JMap[String, Object]]
165
+ // `connpath` locates the connection object inside the response
166
+ // envelope (data.<field>); the cursor/more paths are relative to it.
167
+ var conn: Object = body
168
+ page.get("connpath") match {
169
+ case cp: String if cp.nonEmpty =>
170
+ val sub = Struct.getpath(body, cp)
171
+ if (sub != null) conn = sub
172
+ case _ =>
173
+ }
174
+
175
+ page.get("cursor") match {
176
+ case cur: String if cur.nonEmpty =>
177
+ val c = Struct.getpath(conn, cur)
178
+ if (c != null) paging.put("cursor", c)
179
+ case _ =>
180
+ }
181
+
182
+ page.get("more") match {
183
+ case mp: String if mp.nonEmpty =>
184
+ Struct.getpath(conn, mp) match {
185
+ case b: java.lang.Boolean =>
186
+ paging.put("hasMore", b)
187
+ explicitMore = true
188
+ case _ =>
189
+ }
190
+ case _ =>
191
+ }
192
+ case _ =>
193
+ }
194
+
101
195
  // Body-level cursors.
102
196
  body match {
103
197
  case bm0: JMap[_, _] =>
@@ -112,13 +206,20 @@ class PagingFeature extends BaseFeature("paging", "0.0.1", true) {
112
206
  paging.put("cursor", bm.get("nextCursor"))
113
207
  }
114
208
  bm.get("hasMore") match {
115
- case b: java.lang.Boolean => paging.put("hasMore", b)
209
+ case b: java.lang.Boolean =>
210
+ paging.put("hasMore", b)
211
+ explicitMore = true
116
212
  case _ =>
117
213
  }
118
214
  case _ =>
119
215
  }
120
216
 
121
- if (!(java.lang.Boolean.TRUE == paging.get("hasMore"))
217
+ // Cursor presence only INFERS another page. When the server stated the
218
+ // answer outright — relay's `hasNextPage: false`, or a body `hasMore` —
219
+ // that wins: a final page normally carries both an end cursor and
220
+ // hasNextPage false, and inferring from the cursor there would send the
221
+ // caller back for a page that does not exist, forever.
222
+ if (!explicitMore && !(java.lang.Boolean.TRUE == paging.get("hasMore"))
122
223
  && (paging.get("next") != null || paging.get("cursor") != null
123
224
  || paging.get("nextPage") != null)) {
124
225
  paging.put("hasMore", java.lang.Boolean.TRUE)
@@ -0,0 +1,160 @@
1
+ package SCALAPACKAGE.utility
2
+
3
+ import java.util.{ArrayList, LinkedHashMap, List => JList, Map => JMap}
4
+ import SCALAPACKAGE.core._
5
+ import SCALAPACKAGE.utility.struct.Struct
6
+
7
+ // GraphQL transport. API-INDEPENDENT: every GraphQL SDK this generator
8
+ // produces uses this file unchanged. The API-specific part — which
9
+ // operations exist and what each one's document is — is model data,
10
+ // computed once by apidef and emitted into Config.
11
+ //
12
+ // Two jobs:
13
+ //
14
+ // graphqlBody — build { query, variables } for a point, binding the
15
+ // op's arguments to the document's declared variables.
16
+ //
17
+ // graphqlErrors — lift a GraphQL failure into an SDK error. GraphQL
18
+ // reports failures as a top-level `errors` array under
19
+ // HTTP 200, so the status-driven path in resultBasic
20
+ // never sees them.
21
+ object Graphql {
22
+
23
+ // Content type every GraphQL-over-HTTP request uses.
24
+ val ContentType: String = "application/json"
25
+
26
+ // Map a GraphQL error to the same error codes the HTTP path produces, so
27
+ // a caller handles auth or rate limiting identically on both transports.
28
+ // Servers put the machine-readable code in `extensions.code`;
29
+ // Linear-style APIs use `extensions.type`.
30
+ def errorCode(gqlerr: Object): String = {
31
+ val ext = Struct.getprop(gqlerr, "extensions")
32
+
33
+ var code = Struct.getprop(ext, "code") match {
34
+ case s: String => s
35
+ case _ => ""
36
+ }
37
+ if (code == null || code.isEmpty) {
38
+ code = Struct.getprop(ext, "type") match {
39
+ case s: String => s
40
+ case _ => ""
41
+ }
42
+ }
43
+ val raw = code.toUpperCase
44
+
45
+ if (raw.contains("AUTH") || raw.contains("FORBIDDEN") ||
46
+ raw.contains("UNAUTHENTICATED")) return "request_auth"
47
+ if (raw.contains("RATELIMIT") || raw.contains("RATE_LIMIT") ||
48
+ raw.contains("TOO_MANY")) return "request_ratelimit"
49
+ if (raw.contains("BAD_USER_INPUT") || raw.contains("VALIDATION") ||
50
+ raw.contains("INVALID")) return "request_invalid"
51
+
52
+ "request_graphql"
53
+ }
54
+
55
+ // Build the request body for a GraphQL point.
56
+ //
57
+ // Variables come from the op's own arguments: a named variable binds to
58
+ // the like-named argument (`from`), and the input-object variable (empty
59
+ // `from`) takes the request data as a whole — which is what makes a
60
+ // generated create/update call look exactly like its REST equivalent.
61
+ def graphqlBody(ctx: Context): Object = {
62
+ val gql = Helpers.toMapAny(Struct.getprop(ctx.point, "graphql"))
63
+ if (gql == null) return null
64
+
65
+ // reqmatch/reqdata hold the caller's arguments for THIS call; data/match
66
+ // hold the entity's current state. Which pair depends on whether the op
67
+ // takes match or data input. A named variable falls back to the current
68
+ // state, so updating a loaded entity with just {title} still binds the
69
+ // stored id the mutation requires.
70
+ val datainput = ctx.op != null && "data" == ctx.op.input
71
+ var reqsrc = if (datainput) ctx.reqdata else ctx.reqmatch
72
+ var datasrc = if (datainput) ctx.data else ctx.matchData
73
+ if (reqsrc == null) reqsrc = new LinkedHashMap[String, Object]()
74
+ if (datasrc == null) datasrc = new LinkedHashMap[String, Object]()
75
+
76
+ val variables = new LinkedHashMap[String, Object]()
77
+
78
+ val varlist = Struct.getprop(gql, "vars") match {
79
+ case l: JList[_] => l.asInstanceOf[JList[Object]]
80
+ case _ => new ArrayList[Object]()
81
+ }
82
+
83
+ val it = varlist.iterator()
84
+ while (it.hasNext) {
85
+ val spec = Helpers.toMapAny(it.next())
86
+ if (spec != null) {
87
+ val name = Struct.getprop(spec, "name") match {
88
+ case s: String => s
89
+ case _ => null
90
+ }
91
+ val from = Struct.getprop(spec, "from") match {
92
+ case s: String => s
93
+ case _ => ""
94
+ }
95
+
96
+ if (name != null) {
97
+ if (from == null || from.isEmpty) {
98
+ // The input object IS the request body. Strip the action
99
+ // selector, which is an SDK-side point discriminator, not an
100
+ // API field.
101
+ val body = new LinkedHashMap[String, Object]()
102
+ val ks = reqsrc.keySet().iterator()
103
+ while (ks.hasNext) {
104
+ val k = ks.next()
105
+ if ("$action" != k) body.put(k, reqsrc.get(k))
106
+ }
107
+ variables.put(name, body)
108
+ } else {
109
+ // Only send variables the caller actually supplied: sending an
110
+ // explicit null would clear a field on many APIs.
111
+ var v = Struct.getprop(reqsrc, from)
112
+ if (v == null) v = Struct.getprop(datasrc, from)
113
+ if (v != null) variables.put(name, v)
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ val out = new LinkedHashMap[String, Object]()
120
+ out.put("query", Struct.getprop(gql, "doc"))
121
+ out.put("variables", variables)
122
+ out
123
+ }
124
+
125
+ // Inspect a decoded GraphQL response body and record a failure when the
126
+ // server reported one. Returns true when an error was recorded.
127
+ //
128
+ // Partial data (`data` alongside `errors`) is treated as failure: the
129
+ // REST surface has no partial-success concept, and silently returning
130
+ // half an object would be worse than failing.
131
+ def graphqlErrors(ctx: Context): java.lang.Boolean = {
132
+ val result = ctx.result
133
+ if (result == null || ctx.point == null) return false
134
+
135
+ val kind = Struct.getprop(ctx.point, "kind") match {
136
+ case s: String => s
137
+ case _ => ""
138
+ }
139
+ if ("graphql" != kind) return false
140
+
141
+ val errors = Struct.getprop(result.body, "errors") match {
142
+ case l: JList[_] => l.asInstanceOf[JList[Object]]
143
+ case _ => null
144
+ }
145
+ if (errors == null || errors.isEmpty) return false
146
+
147
+ val first = errors.get(0)
148
+ var msg = Struct.getprop(first, "message") match {
149
+ case s: String => s
150
+ case _ => ""
151
+ }
152
+ if (msg == null || msg.isEmpty) msg = "graphql error"
153
+ if (1 < errors.size) msg = msg + " (+" + (errors.size - 1) + " more)"
154
+
155
+ result.err = ctx.makeError(errorCode(first), "graphql: " + msg)
156
+ result.ok = false
157
+
158
+ true
159
+ }
160
+ }
@@ -198,7 +198,7 @@ object MakeOptions {
198
198
  + "\"headers\": { \"`$CHILD`\": \"`$STRING`\" },"
199
199
  + "\"allow\": {"
200
200
  + " \"method\": \"GET,PUT,POST,PATCH,DELETE,OPTIONS\","
201
- + " \"op\": \"create,update,load,list,remove,command,direct\""
201
+ + " \"op\": \"create,update,load,list,remove,command,direct,graphql\""
202
202
  + "},"
203
203
  + "\"entity\": { \"`$CHILD`\": {"
204
204
  + " \"`$OPEN`\": true, \"active\": false, \"alias\": {} } },"
@@ -363,6 +363,12 @@ object MakeResponse {
363
363
  utility.resultBasic(ctx)
364
364
  utility.resultHeaders(ctx)
365
365
  utility.resultBody(ctx)
366
+
367
+ // GraphQL reports failures as a top-level `errors` array under HTTP
368
+ // 200, so resultBasic's status check never sees them. Lift them here,
369
+ // before the response transform tries to unwrap data that is not there.
370
+ utility.graphqlErrors(ctx)
371
+
366
372
  utility.transformResponse(ctx)
367
373
 
368
374
  if (result.err == null) result.ok = true
@@ -37,8 +37,28 @@ object MakeSpec {
37
37
  ctx.spec.params = utility.prepareParams(ctx)
38
38
  ctx.spec.query = utility.prepareQuery(ctx)
39
39
  ctx.spec.headers = utility.prepareHeaders(ctx)
40
- ctx.spec.body = utility.prepareBody(ctx)
41
- ctx.spec.path = utility.preparePath(ctx)
40
+
41
+ val pkind = Struct.getprop(ctx.point, "kind") match {
42
+ case s: String => s
43
+ case _ => ""
44
+ }
45
+
46
+ if ("graphql" == pkind) {
47
+ // GraphQL addresses one endpoint: no path parts, no query string, and
48
+ // the body carries the operation. prepareBody is skipped deliberately
49
+ // — it only emits a body for data-input ops, whereas every GraphQL op
50
+ // posts one, including load/list/remove.
51
+ ctx.spec.body = utility.graphqlBody(ctx)
52
+ ctx.spec.path = ""
53
+ // prepareQuery already copied the op's match arguments into the query
54
+ // string. Those same values are bound as operation variables, so
55
+ // leaving them would send /graphql?id=i1.
56
+ ctx.spec.query = new LinkedHashMap[String, Object]()
57
+ ctx.spec.headers.put("content-type", Graphql.ContentType)
58
+ } else {
59
+ ctx.spec.body = utility.prepareBody(ctx)
60
+ ctx.spec.path = utility.preparePath(ctx)
61
+ }
42
62
 
43
63
  if (ctx.ctrl.explain != null) ctx.ctrl.explain.put("spec", ctx.spec)
44
64
 
@@ -29,6 +29,8 @@ object Register {
29
29
  u.prepareParams = (ctx) => PrepareParams.prepareParams(ctx)
30
30
  u.preparePath = (ctx) => PreparePath.preparePath(ctx)
31
31
  u.prepareQuery = (ctx) => PrepareQuery.prepareQuery(ctx)
32
+ u.graphqlBody = (ctx) => Graphql.graphqlBody(ctx)
33
+ u.graphqlErrors = (ctx) => Graphql.graphqlErrors(ctx)
32
34
  u.resultBasic = (ctx) => ResultBasic.resultBasic(ctx)
33
35
  u.resultBody = (ctx) => ResultBody.resultBody(ctx)
34
36
  u.resultHeaders = (ctx) => ResultHeaders.resultHeaders(ctx)
@@ -29,6 +29,8 @@ public final class Utility {
29
29
  public var prepareParams: ((Context) -> VMap)!
30
30
  public var preparePath: ((Context) -> String)!
31
31
  public var prepareQuery: ((Context) -> VMap)!
32
+ public var graphqlBody: ((Context) -> Value)!
33
+ public var graphqlErrors: ((Context) -> Bool)!
32
34
  public var resultBasic: ((Context) -> Result)!
33
35
  public var resultBody: ((Context) -> Result)!
34
36
  public var resultHeaders: ((Context) -> Result)!
@@ -70,6 +72,8 @@ public final class Utility {
70
72
  u.prepareParams = src.prepareParams
71
73
  u.preparePath = src.preparePath
72
74
  u.prepareQuery = src.prepareQuery
75
+ u.graphqlBody = src.graphqlBody
76
+ u.graphqlErrors = src.graphqlErrors
73
77
  u.resultBasic = src.resultBasic
74
78
  u.resultBody = src.resultBody
75
79
  u.resultHeaders = src.resultHeaders
@@ -48,6 +48,14 @@ public final class PagingFeature: BaseFeature {
48
48
  // A per-call cursor/page from ctrl takes priority (auto-iteration).
49
49
  let paging = ctx.ctrl.paging
50
50
 
51
+ // GraphQL paginates through operation VARIABLES, not the query string.
52
+ // This hook runs after makeSpec, so spec.body already holds the
53
+ // { query, variables } envelope, and before makeFetchDef serialises it.
54
+ if "graphql" == gp(ctx.point, "kind").asString {
55
+ graphqlPreRequest(ctx, paging)
56
+ return
57
+ }
58
+
51
59
  var cursor: Value = .noval
52
60
  var hasCursor = false
53
61
  if let paging = paging {
@@ -71,6 +79,49 @@ public final class PagingFeature: BaseFeature {
71
79
  }
72
80
  }
73
81
 
82
+ // Relay pagination: the cursor is the `after` variable (or whatever the
83
+ // model named it), and the page size is `first`.
84
+ private func graphqlPreRequest(_ ctx: Context, _ paging: VMap?) {
85
+ guard let spec = ctx.spec, let body = spec.body.asMap else {
86
+ return
87
+ }
88
+
89
+ let variables: VMap
90
+ if let v = body.entries["variables"]?.asMap {
91
+ variables = v
92
+ }
93
+ else {
94
+ variables = VMap()
95
+ body.entries["variables"] = .map(variables)
96
+ }
97
+
98
+ let afterVar = foptStr(options, "afterVar", "after")
99
+ let firstVar = foptStr(options, "firstVar", "first")
100
+
101
+ // Only bind variables the operation actually declares, or the server
102
+ // rejects the document.
103
+ var declared: Set<String> = []
104
+ if let varlist = gpath(ctx.point, "graphql", "vars").asList {
105
+ for v in varlist.items {
106
+ if let name = gp(v, "name").asString {
107
+ declared.insert(name)
108
+ }
109
+ }
110
+ }
111
+
112
+ if let paging = paging {
113
+ let cursor = gp(paging, "cursor")
114
+ if !isNil(cursor) && declared.contains(afterVar) {
115
+ variables.entries[afterVar] = cursor
116
+ }
117
+ }
118
+
119
+ if !isNil(fopt(options, "limit")) && isNil(gp(variables, firstVar))
120
+ && declared.contains(firstVar) {
121
+ variables.entries[firstVar] = .int(Int64(foptInt(options, "limit", 0)))
122
+ }
123
+ }
124
+
74
125
  public override func preResult(_ ctx: Context) {
75
126
  if !active || !isList(ctx) {
76
127
  return
@@ -98,6 +149,34 @@ public final class PagingFeature: BaseFeature {
98
149
  }
99
150
  }
100
151
 
152
+ // Set when the response states hasMore outright, rather than leaving it
153
+ // to be inferred from the presence of a cursor.
154
+ var explicitMore = false
155
+
156
+ // Relay connections carry the cursor in pageInfo, at the path the model
157
+ // recorded for this op.
158
+ if let page = gpath(ctx.point, "graphql", "page").asMap, nil != body.asMap {
159
+ // `connpath` locates the connection object inside the response
160
+ // envelope (data.<field>); the cursor/more paths are relative to it.
161
+ var conn = body
162
+ if let connpath = gp(page, "connpath").asString, !connpath.isEmpty {
163
+ let sub = getpath(body, jtpv(connpath.split(separator: ".").map(String.init)))
164
+ if !isNil(sub) { conn = sub }
165
+ }
166
+
167
+ if let cursorpath = gp(page, "cursor").asString, !cursorpath.isEmpty {
168
+ let c = getpath(conn, jtpv(cursorpath.split(separator: ".").map(String.init)))
169
+ if !isNil(c) { paging.entries["cursor"] = c }
170
+ }
171
+
172
+ if let morepath = gp(page, "more").asString, !morepath.isEmpty {
173
+ if let more = getpath(conn, jtpv(morepath.split(separator: ".").map(String.init))).asBool {
174
+ paging.entries["hasMore"] = .bool(more)
175
+ explicitMore = true
176
+ }
177
+ }
178
+ }
179
+
101
180
  // Body-level cursors.
102
181
  if let bm = body.asMap {
103
182
  let next = gp(bm, "next")
@@ -114,11 +193,17 @@ public final class PagingFeature: BaseFeature {
114
193
  }
115
194
  if let hmb = gp(bm, "hasMore").asBool {
116
195
  paging.entries["hasMore"] = .bool(hmb)
196
+ explicitMore = true
117
197
  }
118
198
  }
119
199
 
200
+ // Cursor presence only INFERS another page. When the server stated the
201
+ // answer outright - relay's `hasNextPage: false`, or a body `hasMore` -
202
+ // that wins: a final page normally carries both an end cursor and
203
+ // hasNextPage false, and inferring from the cursor there would send the
204
+ // caller back for a page that does not exist, forever.
120
205
  let hasMoreNow = gp(paging, "hasMore").asBool ?? false
121
- if !hasMoreNow &&
206
+ if !explicitMore && !hasMoreNow &&
122
207
  (!isNil(gp(paging, "next")) || !isNil(gp(paging, "cursor")) || !isNil(gp(paging, "nextPage"))) {
123
208
  paging.entries["hasMore"] = .bool(true)
124
209
  }
@@ -0,0 +1,142 @@
1
+ import Foundation
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
+ // graphqlBodyUtil — build { query, variables } for a point, binding the
11
+ // op's arguments to the document's declared variables.
12
+ //
13
+ // graphqlErrorsUtil — 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
+ let 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
+ func graphqlErrorCode(_ gqlerr: Value) -> String {
26
+ let ext = gp(gqlerr, "extensions")
27
+
28
+ var code = gp(ext, "code").asString ?? ""
29
+ if code.isEmpty {
30
+ code = gp(ext, "type").asString ?? ""
31
+ }
32
+ let raw = code.uppercased()
33
+
34
+ if raw.contains("AUTH") || raw.contains("FORBIDDEN")
35
+ || raw.contains("UNAUTHENTICATED") {
36
+ return "request_auth"
37
+ }
38
+ if raw.contains("RATELIMIT") || raw.contains("RATE_LIMIT")
39
+ || raw.contains("TOO_MANY") {
40
+ return "request_ratelimit"
41
+ }
42
+ if raw.contains("BAD_USER_INPUT") || raw.contains("VALIDATION")
43
+ || raw.contains("INVALID") {
44
+ return "request_invalid"
45
+ }
46
+
47
+ return "request_graphql"
48
+ }
49
+
50
+ /// Build the request body for a GraphQL point.
51
+ ///
52
+ /// Variables come from the op's own arguments: a named variable binds to the
53
+ /// like-named argument (`from`), and the input-object variable (empty
54
+ /// `from`) takes the request data as a whole — which is what makes a
55
+ /// generated create/update call look exactly like its REST equivalent.
56
+ func graphqlBodyUtil(_ ctx: Context) -> Value {
57
+ let gql = gp(ctx.point, "graphql")
58
+ guard nil != gql.asMap else { return .noval }
59
+
60
+ // reqmatch/reqdata hold the caller's arguments for THIS call; data/match
61
+ // hold the entity's current state. Which pair depends on whether the op
62
+ // takes match or data input. A named variable falls back to the current
63
+ // state, so updating a loaded entity with just {title} still binds the
64
+ // stored id the mutation requires.
65
+ var reqsrc = ctx.reqmatch
66
+ var datasrc = ctx.match
67
+ if let op = ctx.op, "data" == op.input {
68
+ reqsrc = ctx.reqdata
69
+ datasrc = ctx.data
70
+ }
71
+
72
+ let variables = VMap()
73
+
74
+ if let varlist = gp(gql, "vars").asList {
75
+ for spec in varlist.items {
76
+ guard nil != spec.asMap else { continue }
77
+
78
+ let name = gp(spec, "name").asString ?? ""
79
+ if name.isEmpty { continue }
80
+
81
+ let from = gp(spec, "from").asString ?? ""
82
+
83
+ if from.isEmpty {
84
+ // The input object IS the request body. Strip the action selector,
85
+ // which is an SDK-side point discriminator, not an API field.
86
+ let body = VMap()
87
+ for item in items(.map(reqsrc)) {
88
+ let key = item[0].asString ?? ""
89
+ if "$action" != key {
90
+ body.entries[key] = item[1]
91
+ }
92
+ }
93
+ variables.entries[name] = .map(body)
94
+ continue
95
+ }
96
+
97
+ // Only send variables the caller actually supplied: sending an
98
+ // explicit null would clear a field on many APIs.
99
+ var val = gp(.map(reqsrc), from)
100
+ if isNil(val) {
101
+ val = gp(.map(datasrc), from)
102
+ }
103
+ if !isNil(val) {
104
+ variables.entries[name] = val
105
+ }
106
+ }
107
+ }
108
+
109
+ let out = VMap()
110
+ out.entries["query"] = gp(gql, "doc")
111
+ out.entries["variables"] = .map(variables)
112
+
113
+ return .map(out)
114
+ }
115
+
116
+ /// Inspect a decoded GraphQL response body and record a failure when the
117
+ /// server reported one. Returns true when an error was recorded.
118
+ ///
119
+ /// Partial data (`data` alongside `errors`) is treated as failure: the REST
120
+ /// surface has no partial-success concept, and silently returning half an
121
+ /// object would be worse than failing.
122
+ @discardableResult
123
+ func graphqlErrorsUtil(_ ctx: Context) -> Bool {
124
+ guard let result = ctx.result else { return false }
125
+
126
+ guard "graphql" == gp(ctx.point, "kind").asString else { return false }
127
+
128
+ guard let errors = gp(result.body, "errors").asList else { return false }
129
+ if errors.items.isEmpty { return false }
130
+
131
+ let first = errors.items[0]
132
+ var msg = gp(first, "message").asString ?? ""
133
+ if msg.isEmpty { msg = "graphql error" }
134
+ if 1 < errors.items.count {
135
+ msg = msg + " (+\(errors.items.count - 1) more)"
136
+ }
137
+
138
+ result.err = ctx.makeError(graphqlErrorCode(first), "graphql: " + msg)
139
+ result.ok = false
140
+
141
+ return true
142
+ }