@voxgig/sdkgen 4.34.1 → 4.34.2
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/bin/voxgig-sdkgen +1 -1
- package/dist/cmp/ReadmeErrors.js +3 -3
- package/dist/cmp/ReadmeErrors.js.map +1 -1
- package/dist/cmp/ReadmeExplanation.js +3 -3
- package/dist/cmp/ReadmeExplanation.js.map +1 -1
- package/dist/cmp/ReadmeQuick.js +3 -1
- package/dist/cmp/ReadmeQuick.js.map +1 -1
- package/dist/cmp/ReadmeTop.js +9 -8
- package/dist/cmp/ReadmeTop.js.map +1 -1
- package/dist/helpers/definitionPlan.js +8 -4
- package/dist/helpers/definitionPlan.js.map +1 -1
- package/dist/helpers/naming.d.ts +2 -1
- package/dist/helpers/naming.js +11 -4
- package/dist/helpers/naming.js.map +1 -1
- package/dist/helpers/opExample.d.ts +16 -2
- package/dist/helpers/opExample.js +80 -21
- package/dist/helpers/opExample.js.map +1 -1
- package/dist/helpers/opShape.d.ts +3 -1
- package/dist/helpers/opShape.js +21 -8
- package/dist/helpers/opShape.js.map +1 -1
- package/dist/sdkgen.d.ts +4 -4
- package/dist/sdkgen.js +12 -4
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utility.d.ts +2 -1
- package/dist/utility.js +29 -3
- package/dist/utility.js.map +1 -1
- package/package.json +2 -2
- package/project/.sdk/src/cmp/c/ReadmeEntity_c.ts +3 -2
- package/project/.sdk/src/cmp/c/ReadmeHowto_c.ts +7 -3
- package/project/.sdk/src/cmp/c/ReadmeQuick_c.ts +3 -2
- package/project/.sdk/src/cmp/c/ReadmeRef_c.ts +5 -4
- package/project/.sdk/src/cmp/c/ReadmeTopQuick_c.ts +3 -2
- package/project/.sdk/src/cmp/c/ReadmeTopTest_c.ts +8 -7
- package/project/.sdk/src/cmp/c/utility_c.ts +21 -2
- package/project/.sdk/src/cmp/clojure/ReadmeEntity_clojure.ts +4 -1
- package/project/.sdk/src/cmp/clojure/ReadmeHowto_clojure.ts +17 -6
- package/project/.sdk/src/cmp/clojure/ReadmeModel_clojure.ts +6 -6
- package/project/.sdk/src/cmp/clojure/ReadmeQuick_clojure.ts +13 -8
- package/project/.sdk/src/cmp/clojure/ReadmeRef_clojure.ts +21 -18
- package/project/.sdk/src/cmp/clojure/ReadmeTopQuick_clojure.ts +6 -3
- package/project/.sdk/src/cmp/clojure/ReadmeTopTest_clojure.ts +16 -9
- package/project/.sdk/src/cmp/clojure/TestClean_clojure.ts +4 -4
- package/project/.sdk/src/cmp/clojure/fragment/Entity.fragment.clj +2 -1
- package/project/.sdk/src/cmp/clojure/utility_clojure.ts +14 -0
- package/project/.sdk/src/cmp/cpp/ReadmeEntity_cpp.ts +3 -2
- package/project/.sdk/src/cmp/cpp/ReadmeHowto_cpp.ts +7 -3
- package/project/.sdk/src/cmp/cpp/ReadmeQuick_cpp.ts +3 -2
- package/project/.sdk/src/cmp/cpp/ReadmeRef_cpp.ts +6 -5
- package/project/.sdk/src/cmp/cpp/ReadmeTopQuick_cpp.ts +3 -2
- package/project/.sdk/src/cmp/cpp/ReadmeTopTest_cpp.ts +8 -7
- package/project/.sdk/src/cmp/cpp/utility_cpp.ts +13 -0
- package/project/.sdk/src/cmp/csharp/ReadmeEntity_csharp.ts +3 -2
- package/project/.sdk/src/cmp/csharp/ReadmeHowto_csharp.ts +20 -8
- package/project/.sdk/src/cmp/csharp/ReadmeQuick_csharp.ts +13 -8
- package/project/.sdk/src/cmp/csharp/ReadmeRef_csharp.ts +10 -5
- package/project/.sdk/src/cmp/csharp/ReadmeTopQuick_csharp.ts +14 -7
- package/project/.sdk/src/cmp/csharp/ReadmeTopTest_csharp.ts +18 -10
- package/project/.sdk/src/cmp/csharp/utility_csharp.ts +13 -0
- package/project/.sdk/src/cmp/elixir/ReadmeEntity_elixir.ts +4 -4
- package/project/.sdk/src/cmp/elixir/ReadmeExplanation_elixir.ts +2 -1
- package/project/.sdk/src/cmp/elixir/ReadmeHowto_elixir.ts +13 -6
- package/project/.sdk/src/cmp/elixir/ReadmeModel_elixir.ts +5 -5
- package/project/.sdk/src/cmp/elixir/ReadmeQuick_elixir.ts +13 -10
- package/project/.sdk/src/cmp/elixir/ReadmeRef_elixir.ts +11 -11
- package/project/.sdk/src/cmp/elixir/ReadmeTopQuick_elixir.ts +8 -6
- package/project/.sdk/src/cmp/elixir/ReadmeTopTest_elixir.ts +14 -10
- package/project/.sdk/src/cmp/elixir/fragment/EntityCreateOp.fragment.ex +4 -3
- package/project/.sdk/src/cmp/elixir/fragment/EntityListOp.fragment.ex +4 -3
- package/project/.sdk/src/cmp/elixir/fragment/EntityLoadOp.fragment.ex +4 -3
- package/project/.sdk/src/cmp/elixir/fragment/EntityPatchOp.fragment.ex +4 -3
- package/project/.sdk/src/cmp/elixir/fragment/EntityRemoveOp.fragment.ex +4 -3
- package/project/.sdk/src/cmp/elixir/fragment/EntityUpdateOp.fragment.ex +4 -3
- package/project/.sdk/src/cmp/elixir/utility_elixir.ts +12 -0
- package/project/.sdk/src/cmp/go/ReadmeEntity_go.ts +8 -5
- package/project/.sdk/src/cmp/go/ReadmeExplanation_go.ts +3 -1
- package/project/.sdk/src/cmp/go/ReadmeHowto_go.ts +20 -8
- package/project/.sdk/src/cmp/go/ReadmeModel_go.ts +16 -13
- package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +14 -13
- package/project/.sdk/src/cmp/go/ReadmeRef_go.ts +14 -13
- package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +10 -8
- package/project/.sdk/src/cmp/go/ReadmeTopTest_go.ts +6 -2
- package/project/.sdk/src/cmp/go/utility_go.ts +1 -1
- package/project/.sdk/src/cmp/java/ReadmeEntity_java.ts +8 -19
- package/project/.sdk/src/cmp/java/ReadmeHowto_java.ts +19 -22
- package/project/.sdk/src/cmp/java/ReadmeModel_java.ts +10 -9
- package/project/.sdk/src/cmp/java/ReadmeQuick_java.ts +24 -28
- package/project/.sdk/src/cmp/java/ReadmeRef_java.ts +27 -35
- package/project/.sdk/src/cmp/java/ReadmeTopQuick_java.ts +22 -25
- package/project/.sdk/src/cmp/java/ReadmeTopTest_java.ts +18 -24
- package/project/.sdk/src/cmp/java/utility_java.ts +25 -0
- package/project/.sdk/src/cmp/js/EntityOperation_js.ts +3 -1
- package/project/.sdk/src/cmp/js/ReadmeEntity_js.ts +2 -8
- package/project/.sdk/src/cmp/js/ReadmeHowto_js.ts +11 -9
- package/project/.sdk/src/cmp/js/ReadmeModel_js.ts +9 -9
- package/project/.sdk/src/cmp/js/ReadmeQuick_js.ts +7 -13
- package/project/.sdk/src/cmp/js/ReadmeRef_js.ts +14 -15
- package/project/.sdk/src/cmp/js/ReadmeTopQuick_js.ts +16 -11
- package/project/.sdk/src/cmp/js/ReadmeTopTest_js.ts +11 -7
- package/project/.sdk/src/cmp/js/fragment/EntityCreateOp.fragment.js +1 -1
- package/project/.sdk/src/cmp/js/fragment/EntityListOp.fragment.js +1 -1
- package/project/.sdk/src/cmp/js/fragment/EntityLoadOp.fragment.js +1 -1
- package/project/.sdk/src/cmp/js/fragment/EntityPatchOp.fragment.js +1 -1
- package/project/.sdk/src/cmp/js/fragment/EntityRemoveOp.fragment.js +1 -1
- package/project/.sdk/src/cmp/js/fragment/EntityUpdateOp.fragment.js +1 -1
- package/project/.sdk/src/cmp/js/utility_js.ts +2 -1
- package/project/.sdk/src/cmp/kotlin/ReadmeEntity_kotlin.ts +3 -2
- package/project/.sdk/src/cmp/kotlin/ReadmeHowto_kotlin.ts +19 -9
- package/project/.sdk/src/cmp/kotlin/ReadmeModel_kotlin.ts +10 -9
- package/project/.sdk/src/cmp/kotlin/ReadmeQuick_kotlin.ts +14 -9
- package/project/.sdk/src/cmp/kotlin/ReadmeRef_kotlin.ts +18 -15
- package/project/.sdk/src/cmp/kotlin/ReadmeTopQuick_kotlin.ts +14 -8
- package/project/.sdk/src/cmp/kotlin/ReadmeTopTest_kotlin.ts +17 -10
- package/project/.sdk/src/cmp/kotlin/utility_kotlin.ts +21 -1
- package/project/.sdk/src/cmp/lua/ReadmeEntity_lua.ts +3 -2
- package/project/.sdk/src/cmp/lua/ReadmeExamplesTest_lua.ts +7 -4
- package/project/.sdk/src/cmp/lua/ReadmeHowto_lua.ts +9 -7
- package/project/.sdk/src/cmp/lua/ReadmeModel_lua.ts +13 -13
- package/project/.sdk/src/cmp/lua/ReadmeQuick_lua.ts +20 -9
- package/project/.sdk/src/cmp/lua/ReadmeRef_lua.ts +9 -8
- package/project/.sdk/src/cmp/lua/ReadmeTopQuick_lua.ts +15 -7
- package/project/.sdk/src/cmp/lua/ReadmeTopTest_lua.ts +6 -5
- package/project/.sdk/src/cmp/lua/fragment/Entity.fragment.lua +1 -0
- package/project/.sdk/src/cmp/lua/fragment/EntityCreateOp.fragment.lua +1 -1
- package/project/.sdk/src/cmp/lua/fragment/EntityListOp.fragment.lua +1 -1
- package/project/.sdk/src/cmp/lua/fragment/EntityLoadOp.fragment.lua +1 -1
- package/project/.sdk/src/cmp/lua/fragment/EntityPatchOp.fragment.lua +1 -1
- package/project/.sdk/src/cmp/lua/fragment/EntityRemoveOp.fragment.lua +1 -1
- package/project/.sdk/src/cmp/lua/fragment/EntityUpdateOp.fragment.lua +1 -1
- package/project/.sdk/src/cmp/ocaml/ReadmeEntity_ocaml.ts +3 -2
- package/project/.sdk/src/cmp/ocaml/ReadmeHowto_ocaml.ts +3 -2
- package/project/.sdk/src/cmp/ocaml/ReadmeModel_ocaml.ts +1 -0
- package/project/.sdk/src/cmp/ocaml/ReadmeQuick_ocaml.ts +5 -3
- package/project/.sdk/src/cmp/ocaml/ReadmeRef_ocaml.ts +10 -8
- package/project/.sdk/src/cmp/ocaml/ReadmeTopQuick_ocaml.ts +4 -2
- package/project/.sdk/src/cmp/ocaml/ReadmeTopTest_ocaml.ts +3 -2
- package/project/.sdk/src/cmp/ocaml/utility_ocaml.ts +13 -1
- package/project/.sdk/src/cmp/perl/ReadmeEntity_perl.ts +4 -1
- package/project/.sdk/src/cmp/perl/ReadmeHowto_perl.ts +10 -5
- package/project/.sdk/src/cmp/perl/ReadmeModel_perl.ts +6 -6
- package/project/.sdk/src/cmp/perl/ReadmeQuick_perl.ts +8 -5
- package/project/.sdk/src/cmp/perl/ReadmeRef_perl.ts +21 -18
- package/project/.sdk/src/cmp/perl/ReadmeTopQuick_perl.ts +6 -3
- package/project/.sdk/src/cmp/perl/ReadmeTopTest_perl.ts +11 -8
- package/project/.sdk/src/cmp/perl/fragment/Entity.fragment.pm +1 -1
- package/project/.sdk/src/cmp/perl/fragment/EntityCreateOp.fragment.pm +2 -2
- package/project/.sdk/src/cmp/perl/fragment/EntityLoadOp.fragment.pm +2 -2
- package/project/.sdk/src/cmp/perl/fragment/EntityPatchOp.fragment.pm +2 -2
- package/project/.sdk/src/cmp/perl/fragment/EntityRemoveOp.fragment.pm +1 -1
- package/project/.sdk/src/cmp/perl/fragment/EntityUpdateOp.fragment.pm +2 -2
- package/project/.sdk/src/cmp/perl/utility_perl.ts +11 -1
- package/project/.sdk/src/cmp/php/EntityOperation_php.ts +3 -1
- package/project/.sdk/src/cmp/php/ReadmeEntity_php.ts +4 -3
- package/project/.sdk/src/cmp/php/ReadmeHowto_php.ts +12 -6
- package/project/.sdk/src/cmp/php/ReadmeModel_php.ts +9 -9
- package/project/.sdk/src/cmp/php/ReadmeQuick_php.ts +3 -2
- package/project/.sdk/src/cmp/php/ReadmeRef_php.ts +15 -14
- package/project/.sdk/src/cmp/php/ReadmeTopQuick_php.ts +5 -4
- package/project/.sdk/src/cmp/php/ReadmeTopTest_php.ts +13 -7
- package/project/.sdk/src/cmp/php/fragment/EntityCreateOp.fragment.php +2 -2
- package/project/.sdk/src/cmp/php/fragment/EntityListOp.fragment.php +2 -2
- package/project/.sdk/src/cmp/php/fragment/EntityLoadOp.fragment.php +2 -2
- package/project/.sdk/src/cmp/php/fragment/EntityPatchOp.fragment.php +2 -2
- package/project/.sdk/src/cmp/php/fragment/EntityRemoveOp.fragment.php +2 -2
- package/project/.sdk/src/cmp/php/fragment/EntityUpdateOp.fragment.php +2 -2
- package/project/.sdk/src/cmp/py/EntityOperation_py.ts +3 -1
- package/project/.sdk/src/cmp/py/ReadmeEntity_py.ts +3 -8
- package/project/.sdk/src/cmp/py/ReadmeHowto_py.ts +9 -9
- package/project/.sdk/src/cmp/py/ReadmeModel_py.ts +9 -9
- package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +9 -14
- package/project/.sdk/src/cmp/py/ReadmeRef_py.ts +23 -30
- package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +7 -12
- package/project/.sdk/src/cmp/py/ReadmeTopTest_py.ts +8 -7
- package/project/.sdk/src/cmp/py/fragment/EntityCreateOp.fragment.py +1 -1
- package/project/.sdk/src/cmp/py/fragment/EntityListOp.fragment.py +1 -1
- package/project/.sdk/src/cmp/py/fragment/EntityLoadOp.fragment.py +1 -1
- package/project/.sdk/src/cmp/py/fragment/EntityPatchOp.fragment.py +1 -1
- package/project/.sdk/src/cmp/py/fragment/EntityRemoveOp.fragment.py +1 -1
- package/project/.sdk/src/cmp/py/fragment/EntityUpdateOp.fragment.py +1 -1
- package/project/.sdk/src/cmp/rb/EntityOperation_rb.ts +3 -1
- package/project/.sdk/src/cmp/rb/EntityTypes_rb.ts +2 -1
- package/project/.sdk/src/cmp/rb/ReadmeEntity_rb.ts +4 -1
- package/project/.sdk/src/cmp/rb/ReadmeExamplesTest_rb.ts +3 -2
- package/project/.sdk/src/cmp/rb/ReadmeHowto_rb.ts +20 -10
- package/project/.sdk/src/cmp/rb/ReadmeModel_rb.ts +10 -9
- package/project/.sdk/src/cmp/rb/ReadmeQuick_rb.ts +6 -3
- package/project/.sdk/src/cmp/rb/ReadmeRef_rb.ts +14 -11
- package/project/.sdk/src/cmp/rb/ReadmeTopQuick_rb.ts +6 -3
- package/project/.sdk/src/cmp/rb/ReadmeTopTest_rb.ts +14 -10
- package/project/.sdk/src/cmp/rb/fragment/EntityCreateOp.fragment.rb +2 -1
- package/project/.sdk/src/cmp/rb/fragment/EntityListOp.fragment.rb +1 -1
- package/project/.sdk/src/cmp/rb/fragment/EntityLoadOp.fragment.rb +2 -1
- package/project/.sdk/src/cmp/rb/fragment/EntityPatchOp.fragment.rb +2 -1
- package/project/.sdk/src/cmp/rb/fragment/EntityRemoveOp.fragment.rb +2 -1
- package/project/.sdk/src/cmp/rb/fragment/EntityUpdateOp.fragment.rb +2 -1
- package/project/.sdk/src/cmp/rb/utility_rb.ts +10 -0
- package/project/.sdk/src/cmp/rust/ReadmeEntity_rust.ts +3 -2
- package/project/.sdk/src/cmp/rust/ReadmeHowto_rust.ts +6 -2
- package/project/.sdk/src/cmp/rust/ReadmeQuick_rust.ts +3 -2
- package/project/.sdk/src/cmp/rust/ReadmeRef_rust.ts +5 -4
- package/project/.sdk/src/cmp/rust/ReadmeTopQuick_rust.ts +3 -2
- package/project/.sdk/src/cmp/rust/ReadmeTopTest_rust.ts +7 -6
- package/project/.sdk/src/cmp/rust/utility_rust.ts +13 -1
- package/project/.sdk/src/cmp/scala/EntityTypes_scala.ts +5 -2
- package/project/.sdk/src/cmp/scala/ReadmeEntity_scala.ts +8 -19
- package/project/.sdk/src/cmp/scala/ReadmeHowto_scala.ts +17 -22
- package/project/.sdk/src/cmp/scala/ReadmeModel_scala.ts +10 -9
- package/project/.sdk/src/cmp/scala/ReadmeQuick_scala.ts +18 -28
- package/project/.sdk/src/cmp/scala/ReadmeRef_scala.ts +25 -35
- package/project/.sdk/src/cmp/scala/ReadmeTopQuick_scala.ts +14 -25
- package/project/.sdk/src/cmp/scala/ReadmeTopTest_scala.ts +16 -24
- package/project/.sdk/src/cmp/scala/utility_scala.ts +25 -0
- package/project/.sdk/src/cmp/swift/EntityTypes_swift.ts +1 -1
- package/project/.sdk/src/cmp/swift/ReadmeEntity_swift.ts +3 -2
- package/project/.sdk/src/cmp/swift/ReadmeHowto_swift.ts +22 -8
- package/project/.sdk/src/cmp/swift/ReadmeModel_swift.ts +10 -9
- package/project/.sdk/src/cmp/swift/ReadmeQuick_swift.ts +16 -8
- package/project/.sdk/src/cmp/swift/ReadmeRef_swift.ts +20 -15
- package/project/.sdk/src/cmp/swift/ReadmeTopQuick_swift.ts +12 -7
- package/project/.sdk/src/cmp/swift/ReadmeTopTest_swift.ts +21 -10
- package/project/.sdk/src/cmp/swift/utility_swift.ts +14 -1
- package/project/.sdk/src/cmp/ts/ReadmeEntity_ts.ts +2 -8
- package/project/.sdk/src/cmp/ts/ReadmeHowto_ts.ts +11 -9
- package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +9 -9
- package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +7 -13
- package/project/.sdk/src/cmp/ts/ReadmeRef_ts.ts +14 -23
- package/project/.sdk/src/cmp/ts/ReadmeTopQuick_ts.ts +8 -14
- package/project/.sdk/src/cmp/ts/ReadmeTopTest_ts.ts +29 -14
- package/project/.sdk/src/cmp/ts/fragment/EntityCreateOp.fragment.ts +1 -1
- package/project/.sdk/src/cmp/ts/fragment/EntityListOp.fragment.ts +1 -1
- package/project/.sdk/src/cmp/ts/fragment/EntityLoadOp.fragment.ts +1 -1
- package/project/.sdk/src/cmp/ts/fragment/EntityPatchOp.fragment.ts +1 -1
- package/project/.sdk/src/cmp/ts/fragment/EntityUpdateOp.fragment.ts +1 -1
- package/project/.sdk/src/cmp/ts/utility_ts.ts +2 -1
- package/project/.sdk/src/cmp/zig/ReadmeEntity_zig.ts +3 -2
- package/project/.sdk/src/cmp/zig/ReadmeHowto_zig.ts +6 -2
- package/project/.sdk/src/cmp/zig/ReadmeQuick_zig.ts +4 -3
- package/project/.sdk/src/cmp/zig/ReadmeRef_zig.ts +5 -4
- package/project/.sdk/src/cmp/zig/ReadmeTopQuick_zig.ts +3 -2
- package/project/.sdk/src/cmp/zig/ReadmeTopTest_zig.ts +7 -6
- package/project/.sdk/src/cmp/zig/utility_zig.ts +13 -0
- package/project/.sdk/tm/c/utility/transform_request.c +1 -1
- package/project/.sdk/tm/clojure/src/sdk/core.clj +32 -26
- package/project/.sdk/tm/clojure/test/sdk/test/concurrency.clj +136 -0
- package/project/.sdk/tm/clojure/test/sdk/test_runner.clj +3 -1
- package/project/.sdk/tm/cpp/core/types.hpp +36 -6
- package/project/.sdk/tm/cpp/test/concurrency_test.cpp +348 -0
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +42 -25
- package/project/.sdk/tm/csharp/core/Context.cs +8 -5
- package/project/.sdk/tm/csharp/test/ConcurrencyTest.cs +238 -0
- package/project/.sdk/tm/csharp/utility/Clean.cs +12 -10
- package/project/.sdk/tm/csharp/utility/TransformRequest.cs +1 -1
- package/project/.sdk/tm/elixir/lib/projectname/context.ex +3 -0
- package/project/.sdk/tm/elixir/lib/projectname/entity_base.ex +2 -1
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +31 -10
- package/project/.sdk/tm/elixir/test/concurrency_test.exs +176 -0
- package/project/.sdk/tm/go/core/context.go +5 -1
- package/project/.sdk/tm/go/test/concurrency_test.go +214 -0
- package/project/.sdk/tm/go/utility/transform_request.go +1 -1
- package/project/.sdk/tm/java/core/Context.java +6 -3
- package/project/.sdk/tm/java/test/ConcurrencyTest.java +221 -0
- package/project/.sdk/tm/java/utility/Clean.java +22 -17
- package/project/.sdk/tm/java/utility/TransformRequest.java +1 -1
- package/project/.sdk/tm/js/src/utility/TransformRequestUtility.js +1 -1
- package/project/.sdk/tm/kotlin/core/Context.kt +5 -3
- package/project/.sdk/tm/kotlin/test/ConcurrencyTest.kt +193 -0
- package/project/.sdk/tm/kotlin/utility/ResultTransform.kt +1 -1
- package/project/.sdk/tm/lua/utility/transform_request.lua +1 -1
- package/project/.sdk/tm/ocaml/Makefile +21 -8
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +27 -12
- package/project/.sdk/tm/ocaml/sdk_types.ml +6 -2
- package/project/.sdk/tm/ocaml/test/concurrency_test.ml +148 -0
- package/project/.sdk/tm/perl/utility/transform_request.pm +1 -1
- package/project/.sdk/tm/php/feature/TestFeature.php +9 -9
- package/project/.sdk/tm/php/utility/TransformRequest.php +1 -1
- package/project/.sdk/tm/py/pkg/core/context.py +2 -2
- package/project/.sdk/tm/py/pkg/utility/clean.py +12 -8
- package/project/.sdk/tm/py/pkg/utility/transform_request.py +1 -1
- package/project/.sdk/tm/py/test/test_concurrency.py +177 -0
- package/project/.sdk/tm/rb/core/context.rb +8 -3
- package/project/.sdk/tm/rb/test/concurrency_test.rb +160 -0
- package/project/.sdk/tm/rb/utility/clean.rb +8 -7
- package/project/.sdk/tm/rb/utility/transform_request.rb +1 -1
- package/project/.sdk/tm/rust/utility/transform_request.rs +1 -1
- package/project/.sdk/tm/scala/Makefile +2 -0
- package/project/.sdk/tm/scala/core/Context.scala +6 -4
- package/project/.sdk/tm/scala/sdktest/ConcurrencyTest.scala +186 -0
- package/project/.sdk/tm/scala/utility/Transform.scala +1 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Context.swift +2 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/ResultUtil.swift +35 -12
- package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/ConcurrencyTest.swift +214 -0
- package/project/.sdk/tm/ts/src/utility/TransformRequestUtility.ts +1 -1
- package/project/.sdk/tm/zig/core/utility.zig +1 -1
- package/project/.sdk/tm/zig/feature/test.zig +10 -8
- package/project/sdkgen-package.json +1 -1
- package/src/cmp/ReadmeErrors.ts +5 -5
- package/src/cmp/ReadmeExplanation.ts +4 -5
- package/src/cmp/ReadmeQuick.ts +3 -1
- package/src/cmp/ReadmeTop.ts +10 -12
- package/src/helpers/definitionPlan.ts +8 -4
- package/src/helpers/naming.ts +12 -4
- package/src/helpers/opExample.ts +97 -25
- package/src/helpers/opShape.ts +25 -9
- package/src/sdkgen.ts +14 -3
- package/src/utility.ts +31 -4
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
package KOTLINPACKAGE.sdktest
|
|
2
|
+
|
|
3
|
+
// Requests in flight at once on one client. Each resolves its operation
|
|
4
|
+
// through the cache the client's root context shares with every request, and
|
|
5
|
+
// registers and cleans secrets through the one registry the client holds.
|
|
6
|
+
|
|
7
|
+
import java.util.concurrent.ConcurrentLinkedQueue
|
|
8
|
+
import java.util.concurrent.CountDownLatch
|
|
9
|
+
import java.util.concurrent.CyclicBarrier
|
|
10
|
+
import java.util.function.BiFunction
|
|
11
|
+
import java.util.function.Supplier
|
|
12
|
+
|
|
13
|
+
import org.junit.jupiter.api.Assertions.assertEquals
|
|
14
|
+
import org.junit.jupiter.api.Assertions.assertSame
|
|
15
|
+
import org.junit.jupiter.api.Assertions.assertTrue
|
|
16
|
+
import org.junit.jupiter.api.Test
|
|
17
|
+
|
|
18
|
+
import KOTLINPACKAGE.core.Context
|
|
19
|
+
import KOTLINPACKAGE.core.Operation
|
|
20
|
+
import KOTLINPACKAGE.core.ProjectNameSDK
|
|
21
|
+
import KOTLINPACKAGE.core.Utility
|
|
22
|
+
|
|
23
|
+
class ConcurrencyTest {
|
|
24
|
+
|
|
25
|
+
private val rounds = 200
|
|
26
|
+
private val width = 8
|
|
27
|
+
private val ops = 32
|
|
28
|
+
|
|
29
|
+
// A live client whose transport answers at once.
|
|
30
|
+
private fun liveClient(): ProjectNameSDK {
|
|
31
|
+
val fetch = BiFunction<String, MutableMap<String, Any?>, MutableMap<String, Any?>> { _, _ ->
|
|
32
|
+
linkedMapOf<String, Any?>(
|
|
33
|
+
"status" to 200,
|
|
34
|
+
"statusText" to "OK",
|
|
35
|
+
"headers" to linkedMapOf<String, Any?>(),
|
|
36
|
+
"json" to Supplier<Any?> { linkedMapOf<String, Any?>("ok" to true) },
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
return ProjectNameSDK(linkedMapOf<String, Any?>(
|
|
40
|
+
"base" to "http://concurrency.test/api",
|
|
41
|
+
"allow" to linkedMapOf<String, Any?>("op" to "direct"),
|
|
42
|
+
"system" to linkedMapOf<String, Any?>("fetch" to fetch),
|
|
43
|
+
))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Runs body on `width` threads released together, and returns what they threw.
|
|
47
|
+
private fun atOnce(body: (Int) -> Unit): List<Throwable> {
|
|
48
|
+
val start = CyclicBarrier(width)
|
|
49
|
+
val thrown = ConcurrentLinkedQueue<Throwable>()
|
|
50
|
+
val threads = (0 until width).map { n ->
|
|
51
|
+
Thread {
|
|
52
|
+
try {
|
|
53
|
+
start.await()
|
|
54
|
+
body(n)
|
|
55
|
+
} catch (t: Throwable) {
|
|
56
|
+
thrown.add(t)
|
|
57
|
+
}
|
|
58
|
+
}.also { it.start() }
|
|
59
|
+
}
|
|
60
|
+
threads.forEach { it.join() }
|
|
61
|
+
return thrown.toList()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@Test
|
|
65
|
+
fun concurrentFirstRequestsSucceed() {
|
|
66
|
+
repeat(rounds) { round ->
|
|
67
|
+
// A fresh client each round, so every request in it is a first request.
|
|
68
|
+
val client = liveClient()
|
|
69
|
+
val results = arrayOfNulls<MutableMap<String, Any?>>(width)
|
|
70
|
+
val thrown = atOnce { n -> results[n] = client.direct(linkedMapOf<String, Any?>("path" to "p$n")) }
|
|
71
|
+
|
|
72
|
+
assertTrue(thrown.isEmpty(), "round $round threw: $thrown")
|
|
73
|
+
results.forEachIndexed { n, res ->
|
|
74
|
+
assertEquals(true, res?.get("ok"), "round $round, request $n failed: $res")
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@Test
|
|
80
|
+
fun concurrentResolutionsShareOneCachedOperation() {
|
|
81
|
+
repeat(rounds) { round ->
|
|
82
|
+
val client = liveClient()
|
|
83
|
+
val utility = client.getUtility()
|
|
84
|
+
val root = client.getRootCtx()
|
|
85
|
+
val got = Array(width) { arrayOfNulls<Operation>(ops) }
|
|
86
|
+
val thrown = atOnce { n ->
|
|
87
|
+
for (k in 0 until ops) {
|
|
88
|
+
got[n][k] = utility.makeContext(linkedMapOf<String, Any?>("opname" to "op$k"), root).op
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
assertTrue(thrown.isEmpty(), "round $round threw: $thrown")
|
|
93
|
+
for (k in 0 until ops) {
|
|
94
|
+
val cached = utility.makeContext(linkedMapOf<String, Any?>("opname" to "op$k"), root).op
|
|
95
|
+
for (n in 0 until width) {
|
|
96
|
+
assertSame(cached, got[n][k], "round $round: op$k resolved to more than one Operation")
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private fun addedSecret(round: Int, n: Int, k: Int) = "ADDED-SECRET-$round-$n-$k"
|
|
103
|
+
|
|
104
|
+
// Registers thread n's secrets, then counts the thread out of registering.
|
|
105
|
+
private fun registerSecrets(
|
|
106
|
+
utility: Utility, root: Context, round: Int, n: Int, registering: CountDownLatch,
|
|
107
|
+
) {
|
|
108
|
+
try {
|
|
109
|
+
for (k in 0 until ops) {
|
|
110
|
+
utility.cleanAdd(root, addedSecret(round, n, k))
|
|
111
|
+
}
|
|
112
|
+
} finally {
|
|
113
|
+
registering.countDown()
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private fun assertEverySecretMasked(utility: Utility, root: Context, round: Int) {
|
|
118
|
+
for (n in 0 until width / 2) {
|
|
119
|
+
for (k in 0 until ops) {
|
|
120
|
+
val added = addedSecret(round, n, k)
|
|
121
|
+
assertEquals("[redacted]", utility.clean(root, added),
|
|
122
|
+
"round $round: $added was registered but not masked")
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Secrets registered on some threads while others clean: every clean masks
|
|
128
|
+
// what was registered before it, the longer secret whole, and no
|
|
129
|
+
// registration is lost.
|
|
130
|
+
@Test
|
|
131
|
+
fun concurrentRegistrationKeepsEverySecretMasked() {
|
|
132
|
+
repeat(rounds / 4) { round ->
|
|
133
|
+
val client = liveClient()
|
|
134
|
+
val utility = client.getUtility()
|
|
135
|
+
val root = client.getRootCtx()
|
|
136
|
+
val inner = "INNER-SECRET-$round"
|
|
137
|
+
utility.cleanAdd(root, inner)
|
|
138
|
+
utility.cleanAdd(root, "OUTER-$inner-TAIL")
|
|
139
|
+
val text = "a $inner b OUTER-$inner-TAIL c"
|
|
140
|
+
val masked = "a [redacted] b [redacted] c"
|
|
141
|
+
assertEquals(masked, utility.clean(root, text))
|
|
142
|
+
|
|
143
|
+
val registering = CountDownLatch(width / 2)
|
|
144
|
+
val wrong = ConcurrentLinkedQueue<Any?>()
|
|
145
|
+
val thrown = atOnce { n ->
|
|
146
|
+
if (n < width / 2) {
|
|
147
|
+
registerSecrets(utility, root, round, n, registering)
|
|
148
|
+
} else {
|
|
149
|
+
while (0L < registering.count) {
|
|
150
|
+
val cleaned = utility.clean(root, text)
|
|
151
|
+
if (masked != cleaned) {
|
|
152
|
+
wrong.add(cleaned)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
assertTrue(thrown.isEmpty(), "round $round threw: $thrown")
|
|
159
|
+
assertTrue(wrong.isEmpty(), "round $round cleaned to: $wrong")
|
|
160
|
+
assertEverySecretMasked(utility, root, round)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Requests on one client while secrets register on it: each request copies
|
|
165
|
+
// the client's options, the registry among them.
|
|
166
|
+
@Test
|
|
167
|
+
fun concurrentRequestsSurviveRegistration() {
|
|
168
|
+
repeat(rounds / 4) { round ->
|
|
169
|
+
val client = liveClient()
|
|
170
|
+
val utility = client.getUtility()
|
|
171
|
+
val root = client.getRootCtx()
|
|
172
|
+
val registering = CountDownLatch(width / 2)
|
|
173
|
+
val failed = ConcurrentLinkedQueue<Any?>()
|
|
174
|
+
val thrown = atOnce { n ->
|
|
175
|
+
if (n < width / 2) {
|
|
176
|
+
registerSecrets(utility, root, round, n, registering)
|
|
177
|
+
} else {
|
|
178
|
+
while (0L < registering.count) {
|
|
179
|
+
val res = client.direct(linkedMapOf<String, Any?>("path" to "p$n"))
|
|
180
|
+
if (true != res["ok"]) {
|
|
181
|
+
failed.add(res)
|
|
182
|
+
break
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
assertTrue(thrown.isEmpty(), "round $round threw: $thrown")
|
|
189
|
+
assertTrue(failed.isEmpty(), "round $round, a request failed: $failed")
|
|
190
|
+
assertEverySecretMasked(utility, root, round)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -68,7 +68,7 @@ private fun stripAction(reqdata: Any?): Any? = omitKeys(reqdata, listOf("\$actio
|
|
|
68
68
|
|
|
69
69
|
// A header, cookie or query argument travels where prepareHeaders or
|
|
70
70
|
// prepareQuery sends it, so the body is built from the request data without
|
|
71
|
-
// it, unless the
|
|
71
|
+
// it, unless the point marks it as a field the body keeps.
|
|
72
72
|
private fun routedArgNames(ctx: Context): List<String> =
|
|
73
73
|
(callArgs(ctx, "header") + callArgs(ctx, "cookie") + callArgs(ctx, "query")).map { it.name }
|
|
74
74
|
.filter { !fieldArg(ctx, it) }
|
|
@@ -48,7 +48,7 @@ end
|
|
|
48
48
|
|
|
49
49
|
-- A header, cookie or query argument travels where prepare_headers_util or
|
|
50
50
|
-- prepare_query_util sends it, so the body is built from the request data
|
|
51
|
-
-- without it, unless the
|
|
51
|
+
-- without it, unless the point marks it as a field the body keeps.
|
|
52
52
|
local function routed_arg_names(ctx)
|
|
53
53
|
local names = {}
|
|
54
54
|
for _, kind in ipairs({ "header", "cookie", "query" }) do
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# Makefile for the ProjectName OCaml SDK (generated by @voxgig/sdkgen).
|
|
2
2
|
#
|
|
3
3
|
# The SDK is dependency-free: it compiles with the stock bytecode compiler
|
|
4
|
-
# (ocamlc) — no opam packages, no dune.
|
|
5
|
-
# run_sdk_test
|
|
6
|
-
# run_omni_smoke
|
|
7
|
-
# run_struct_corpus
|
|
8
|
-
# run_primary_corpus
|
|
4
|
+
# (ocamlc) — no opam packages, no dune. The test executables:
|
|
5
|
+
# run_sdk_test — the SDK pipeline + feature suite (Testutil harness)
|
|
6
|
+
# run_omni_smoke — the vendored omni runner's own failure paths
|
|
7
|
+
# run_struct_corpus — the shared voxgig/struct JSON corpus
|
|
8
|
+
# run_primary_corpus — the shared primary-utility JSON corpus
|
|
9
|
+
# run_concurrency_test — requests on several threads at once on one client
|
|
9
10
|
#
|
|
10
11
|
# The two corpus suites run on the VENDORED @voxgig/omni engine
|
|
11
12
|
# (test/vendor/omni/omni.ml), driven through its native API by the local
|
|
@@ -95,6 +96,11 @@ CLEAN_TESTS = $(sort $(wildcard test/clean_test.ml))
|
|
|
95
96
|
# Wildcard so an absent file is simply skipped, never a missing-target error.
|
|
96
97
|
DOC_TESTS = $(sort $(wildcard test/readme_examples_test.ml))
|
|
97
98
|
|
|
99
|
+
# Requests on several threads at once on one client. Only this executable
|
|
100
|
+
# links the threads library; the SDK itself never does.
|
|
101
|
+
CONCURRENCY_SRC = $(RUNTIME) sdk_config.ml $(ENTITIES) sdk_client.ml sdk_error.ml \
|
|
102
|
+
test/testutil.ml test/concurrency_test.ml
|
|
103
|
+
|
|
98
104
|
# Fixed test order: harness deps first, generated/behaviour tests, the gated
|
|
99
105
|
# feature suites, summary last.
|
|
100
106
|
SDK_TEST = $(RUNTIME) sdk_config.ml $(ENTITIES) sdk_client.ml sdk_error.ml \
|
|
@@ -117,9 +123,9 @@ PRIMARY_SRC = $(RUNTIME) sdk_config.ml $(ENTITIES) sdk_client.ml sdk_error.ml \
|
|
|
117
123
|
SMOKE_SRC = utility/vregex.ml utility/voxgig_struct.ml \
|
|
118
124
|
$(OMNI) test/omni_smoke_test.ml
|
|
119
125
|
|
|
120
|
-
.PHONY: test test-sdk test-omni test-corpus test-primary build lint clean
|
|
126
|
+
.PHONY: test test-sdk test-omni test-corpus test-primary test-concurrency build lint clean
|
|
121
127
|
|
|
122
|
-
test: test-sdk test-omni test-corpus test-primary
|
|
128
|
+
test: test-sdk test-omni test-corpus test-primary test-concurrency
|
|
123
129
|
|
|
124
130
|
test-sdk: run_sdk_test
|
|
125
131
|
./run_sdk_test
|
|
@@ -133,6 +139,9 @@ test-corpus: run_struct_corpus
|
|
|
133
139
|
test-primary: run_primary_corpus
|
|
134
140
|
./run_primary_corpus $(CORPUS)
|
|
135
141
|
|
|
142
|
+
test-concurrency: run_concurrency_test
|
|
143
|
+
./run_concurrency_test
|
|
144
|
+
|
|
136
145
|
# Every executable that links the RUNTIME links the gated feature tier with
|
|
137
146
|
# it: library archives FIRST (an archive must precede the modules that use
|
|
138
147
|
# it), stub objects and link flags LAST.
|
|
@@ -148,6 +157,10 @@ run_struct_corpus: $(CORPUS_SRC)
|
|
|
148
157
|
run_primary_corpus: $(PRIMARY_SRC) $(FEATURE_OBJ)
|
|
149
158
|
$(OCAMLC) $(INC) $(FEATURE_LIB) $(PRIMARY_SRC) $(FEATURE_OBJ) $(FEATURE_LINK) -o run_primary_corpus
|
|
150
159
|
|
|
160
|
+
run_concurrency_test: $(CONCURRENCY_SRC) $(FEATURE_OBJ)
|
|
161
|
+
$(OCAMLC) $(INC) -I +unix -I +threads unix.cma threads.cma $(filter-out unix.cma,$(FEATURE_LIB)) \
|
|
162
|
+
$(CONCURRENCY_SRC) $(FEATURE_OBJ) $(FEATURE_LINK) -o run_concurrency_test
|
|
163
|
+
|
|
151
164
|
# Type-check the SDK library (a clean compile means it is sound).
|
|
152
165
|
build: lint
|
|
153
166
|
lint:
|
|
@@ -159,4 +172,4 @@ clean:
|
|
|
159
172
|
test/vendor/omni/*.cmi test/vendor/omni/*.cmo \
|
|
160
173
|
feature/*.cmi feature/*.cmo feature/*/*/*.cmi feature/*/*/*.cmo \
|
|
161
174
|
feature/*/*/*.o test/feature/*/*.cmi test/feature/*/*.cmo \
|
|
162
|
-
run_sdk_test run_omni_smoke run_struct_corpus run_primary_corpus a.out
|
|
175
|
+
run_sdk_test run_omni_smoke run_struct_corpus run_primary_corpus run_concurrency_test a.out
|
|
@@ -159,10 +159,19 @@ let default_ctxspec () : ctxspec =
|
|
|
159
159
|
cs_reqdata = None; cs_match = None; cs_reqmatch = None; cs_point = None;
|
|
160
160
|
cs_spec = None; cs_result = None; cs_response = None }
|
|
161
161
|
|
|
162
|
+
(* Every request racing to build this Operation gets the one stored first. *)
|
|
163
|
+
let rec store_op (opmap : operation OpMap.t Atomic.t) (key : string) (op : operation) : operation =
|
|
164
|
+
let seen = Atomic.get opmap in
|
|
165
|
+
match OpMap.find_opt key seen with
|
|
166
|
+
| Some stored -> stored
|
|
167
|
+
| None ->
|
|
168
|
+
if Atomic.compare_and_set opmap seen (OpMap.add key op seen) then op
|
|
169
|
+
else store_op opmap key op
|
|
170
|
+
|
|
162
171
|
let resolve_op (ctx : ctx) (opname : string) : operation =
|
|
163
172
|
let entname = match ctx.c_entity with Some e -> e.e_name | None -> "_" in
|
|
164
173
|
let cache_key = entname ^ ":" ^ opname in
|
|
165
|
-
match
|
|
174
|
+
match OpMap.find_opt cache_key (Atomic.get ctx.c_opmap) with
|
|
166
175
|
| Some op -> op
|
|
167
176
|
| None ->
|
|
168
177
|
if opname = "" then new_operation (empty_map ())
|
|
@@ -177,8 +186,7 @@ let resolve_op (ctx : ctx) (opname : string) : operation =
|
|
|
177
186
|
let op = new_operation
|
|
178
187
|
(jo [("entity", Str entname); ("name", Str opname);
|
|
179
188
|
("input", Str inpt); ("points", points)]) in
|
|
180
|
-
|
|
181
|
-
op
|
|
189
|
+
store_op ctx.c_opmap cache_key op
|
|
182
190
|
end
|
|
183
191
|
|
|
184
192
|
let make_context_impl (cs : ctxspec) (basectx : ctx option) : ctx =
|
|
@@ -226,7 +234,7 @@ let make_context_impl (cs : ctxspec) (basectx : ctx option) : ctx =
|
|
|
226
234
|
| _ -> (match basectx with Some b -> b.c_shared | None -> Noval) in
|
|
227
235
|
let opmap =
|
|
228
236
|
match cs.cs_opmap with Some h -> h
|
|
229
|
-
| None -> (match basectx with Some b -> b.c_opmap | None ->
|
|
237
|
+
| None -> (match basectx with Some b -> b.c_opmap | None -> Atomic.make OpMap.empty) in
|
|
230
238
|
let mapof = function Some d -> (match to_map d with Map _ as m -> m | _ -> empty_map ()) | None -> empty_map () in
|
|
231
239
|
let data = mapof cs.cs_data and reqdata = mapof cs.cs_reqdata
|
|
232
240
|
and mtch = mapof cs.cs_match and reqmatch = mapof cs.cs_reqmatch in
|
|
@@ -321,18 +329,25 @@ let clean_forms (value : string) : string list =
|
|
|
321
329
|
if String.length j >= 2 then add (String.sub j 1 (String.length j - 2)));
|
|
322
330
|
!out
|
|
323
331
|
|
|
332
|
+
(* A registration reads the registry list and writes a new one, so requests
|
|
333
|
+
* on other domains registering at once take turns, or one would be dropped.
|
|
334
|
+
* Cleaning reads the list whole and needs no turn. *)
|
|
335
|
+
let clean_registering = Atomic.make false
|
|
336
|
+
|
|
324
337
|
let clean_add_util (ctx : ctx) (value : string) : unit =
|
|
325
338
|
let cfg = clean_config ctx in
|
|
326
339
|
let minlen = match getp cfg "min" with Num n -> int_of_float n | _ -> 4 in
|
|
327
340
|
match getp cfg "values" with
|
|
328
341
|
| List r when String.length value >= minlen ->
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
(
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
342
|
+
while not (Atomic.compare_and_set clean_registering false true) do () done;
|
|
343
|
+
Fun.protect ~finally:(fun () -> Atomic.set clean_registering false) (fun () ->
|
|
344
|
+
let have = str_values (List r) in
|
|
345
|
+
let add = List.filter (fun f -> String.length f >= minlen && not (List.mem f have))
|
|
346
|
+
(clean_forms value) in
|
|
347
|
+
(* Longest first, so a value is never masked by a substring of itself. *)
|
|
348
|
+
if add <> [] then
|
|
349
|
+
r := List.map (fun s -> Str s)
|
|
350
|
+
(List.stable_sort (fun a b -> compare (String.length b) (String.length a)) (have @ add)))
|
|
336
351
|
| _ -> ()
|
|
337
352
|
|
|
338
353
|
let mask_value (cfg : value) (value : string) : string =
|
|
@@ -1011,7 +1026,7 @@ let field_arg (ctx : ctx) (name : string) : bool =
|
|
|
1011
1026
|
|
|
1012
1027
|
(* A header, cookie or query argument travels where prepare_headers_util or
|
|
1013
1028
|
prepare_query_util sends it, so the body is built from the request data
|
|
1014
|
-
without it, unless the
|
|
1029
|
+
without it, unless the point marks it as a field the body keeps. *)
|
|
1015
1030
|
let routed_arg_names (ctx : ctx) : string list =
|
|
1016
1031
|
List.filter (fun name -> not (field_arg ctx name))
|
|
1017
1032
|
(List.map (fun (name, _, _) -> name)
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
open Voxgig_struct
|
|
19
19
|
|
|
20
|
+
module OpMap = Map.Make (String)
|
|
21
|
+
|
|
20
22
|
(* Branded SDK error. Raised as `Sdk_error_exc` on the throwing path; carried
|
|
21
23
|
* as `sdk_error option` on the (value, err) pipeline return tuples. *)
|
|
22
24
|
type sdk_error = {
|
|
@@ -238,7 +240,7 @@ and ctxspec = {
|
|
|
238
240
|
mutable cs_options : value option;
|
|
239
241
|
mutable cs_entity : entity_obj option;
|
|
240
242
|
mutable cs_shared : value option;
|
|
241
|
-
mutable cs_opmap :
|
|
243
|
+
mutable cs_opmap : operation OpMap.t Atomic.t option;
|
|
242
244
|
mutable cs_data : value option;
|
|
243
245
|
mutable cs_reqdata : value option;
|
|
244
246
|
mutable cs_match : value option;
|
|
@@ -261,7 +263,9 @@ and ctx = {
|
|
|
261
263
|
mutable c_config : value;
|
|
262
264
|
mutable c_entopts : value;
|
|
263
265
|
mutable c_options : value;
|
|
264
|
-
|
|
266
|
+
(* Shared by every context of one client, so by requests on other domains:
|
|
267
|
+
* a map replaced whole, never changed in place. *)
|
|
268
|
+
mutable c_opmap : operation OpMap.t Atomic.t;
|
|
265
269
|
mutable c_response : response option;
|
|
266
270
|
mutable c_result : result option;
|
|
267
271
|
mutable c_spec : spec option;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
(* Requests in flight at once on one client. Each resolves its operation
|
|
2
|
+
* through the cache the client's root context shares with every request, and
|
|
3
|
+
* registers and cleans secrets through the one registry the client holds.
|
|
4
|
+
*
|
|
5
|
+
* Threads take turns on one domain and switch when the runtime's tick lands,
|
|
6
|
+
* so each test runs long enough to be switched inside a resolution or a
|
|
7
|
+
* registration. *)
|
|
8
|
+
|
|
9
|
+
open Voxgig_struct
|
|
10
|
+
open Sdk_types
|
|
11
|
+
open Testutil
|
|
12
|
+
|
|
13
|
+
let rounds = 3
|
|
14
|
+
let width = 8
|
|
15
|
+
let ops = 4000
|
|
16
|
+
let seeded = 600
|
|
17
|
+
let added = 100
|
|
18
|
+
let masked = "a [redacted] b [redacted] c"
|
|
19
|
+
|
|
20
|
+
(* Runs body on width threads released together, and returns what they raised. *)
|
|
21
|
+
let at_once (body : int -> unit) : string list =
|
|
22
|
+
let lock = Mutex.create () in
|
|
23
|
+
let ready = Condition.create () in
|
|
24
|
+
let waiting = ref 0 in
|
|
25
|
+
let raised = ref [] in
|
|
26
|
+
let run n =
|
|
27
|
+
Mutex.lock lock;
|
|
28
|
+
incr waiting;
|
|
29
|
+
if !waiting = width then Condition.broadcast ready
|
|
30
|
+
else while !waiting < width do Condition.wait ready lock done;
|
|
31
|
+
Mutex.unlock lock;
|
|
32
|
+
try body n
|
|
33
|
+
with e ->
|
|
34
|
+
Mutex.lock lock;
|
|
35
|
+
raised := fail_msg e :: !raised;
|
|
36
|
+
Mutex.unlock lock
|
|
37
|
+
in
|
|
38
|
+
List.iter Thread.join (List.init width (Thread.create run));
|
|
39
|
+
!raised
|
|
40
|
+
|
|
41
|
+
let root_of (cl : sdk_client) : ctx =
|
|
42
|
+
match cl.cl_rootctx with Some c -> c | None -> failwith "no root context"
|
|
43
|
+
|
|
44
|
+
let resolve (cl : sdk_client) (k : int) : operation =
|
|
45
|
+
let ctx = cl.cl_utility.u_make_context
|
|
46
|
+
{ (Sdk_runtime.default_ctxspec ()) with cs_opname = Some ("op" ^ string_of_int k) }
|
|
47
|
+
cl.cl_rootctx in
|
|
48
|
+
ctx.c_op
|
|
49
|
+
|
|
50
|
+
let () = test "concurrent resolutions share one cached operation" (fun () ->
|
|
51
|
+
for round = 1 to rounds do
|
|
52
|
+
let cl = Sdk_client.test () in
|
|
53
|
+
let got = Array.init width (fun _ -> Array.make ops None) in
|
|
54
|
+
let raised = at_once (fun n ->
|
|
55
|
+
for k = 0 to ops - 1 do got.(n).(k) <- Some (resolve cl k) done) in
|
|
56
|
+
check (Printf.sprintf "round %d raised: %s" round (String.concat "; " raised)) (raised = []);
|
|
57
|
+
for k = 0 to ops - 1 do
|
|
58
|
+
let cached = resolve cl k in
|
|
59
|
+
for n = 0 to width - 1 do
|
|
60
|
+
check (Printf.sprintf "round %d: op%d resolved to more than one Operation" round k)
|
|
61
|
+
(match got.(n).(k) with Some op -> op == cached | None -> false)
|
|
62
|
+
done
|
|
63
|
+
done
|
|
64
|
+
done)
|
|
65
|
+
|
|
66
|
+
let clean_str (cl : sdk_client) (text : string) : string =
|
|
67
|
+
match cl.cl_utility.u_clean (root_of cl) (Str text) with Str s -> s | v -> stringify v
|
|
68
|
+
|
|
69
|
+
(* A live client whose transport answers at once. *)
|
|
70
|
+
let live_client () : sdk_client =
|
|
71
|
+
let fetch = Func (fun _ _ _ _ ->
|
|
72
|
+
Sdk_helpers.jo [
|
|
73
|
+
("status", Num 200.); ("statusText", Str "OK"); ("headers", Sdk_helpers.jo []);
|
|
74
|
+
("json", Sdk_helpers.json_thunk (Sdk_helpers.jo [("ok", Bool true)]))]) in
|
|
75
|
+
Sdk_client.make (Sdk_helpers.jo [
|
|
76
|
+
("base", Str "http://concurrency.test/api");
|
|
77
|
+
("allow", Sdk_helpers.jo [("op", Str "direct")]);
|
|
78
|
+
("system", Sdk_helpers.jo [("fetch", fetch)])])
|
|
79
|
+
|
|
80
|
+
(* Registers thread n's secrets, then counts the thread out of registering. *)
|
|
81
|
+
let register_secrets (cl : sdk_client) (round : int) (n : int) (registering : int Atomic.t) : unit =
|
|
82
|
+
Fun.protect ~finally:(fun () -> Atomic.decr registering) (fun () ->
|
|
83
|
+
for k = 1 to added do
|
|
84
|
+
cl.cl_utility.u_clean_add (root_of cl) (Printf.sprintf "ADDED-SECRET-%d-%d-%d" round n k)
|
|
85
|
+
done)
|
|
86
|
+
|
|
87
|
+
let every_secret_masked (cl : sdk_client) (round : int) : unit =
|
|
88
|
+
for n = 0 to width / 2 - 1 do
|
|
89
|
+
for k = 1 to added do
|
|
90
|
+
let secret = Printf.sprintf "ADDED-SECRET-%d-%d-%d" round n k in
|
|
91
|
+
check_str (Printf.sprintf "round %d: %s was registered but not masked" round secret)
|
|
92
|
+
(clean_str cl secret) "[redacted]"
|
|
93
|
+
done
|
|
94
|
+
done
|
|
95
|
+
|
|
96
|
+
(* Secrets registered on some threads while others clean: every clean masks
|
|
97
|
+
* what was registered before it, the longer secret whole, and no
|
|
98
|
+
* registration is lost. *)
|
|
99
|
+
let () = test "concurrent registration keeps every secret masked" (fun () ->
|
|
100
|
+
for round = 1 to rounds do
|
|
101
|
+
let cl = Sdk_client.test () in
|
|
102
|
+
let add v = cl.cl_utility.u_clean_add (root_of cl) v in
|
|
103
|
+
for k = 1 to seeded do add (Printf.sprintf "SEEDED-SECRET-%d-%d" round k) done;
|
|
104
|
+
let inner = Printf.sprintf "INNER-SECRET-%d" round in
|
|
105
|
+
add inner;
|
|
106
|
+
add ("OUTER-" ^ inner ^ "-TAIL");
|
|
107
|
+
let text = "a " ^ inner ^ " b OUTER-" ^ inner ^ "-TAIL c" in
|
|
108
|
+
check_str (Printf.sprintf "round %d" round) (clean_str cl text) masked;
|
|
109
|
+
let registering = Atomic.make (width / 2) in
|
|
110
|
+
let raised = at_once (fun n ->
|
|
111
|
+
if n < width / 2 then register_secrets cl round n registering
|
|
112
|
+
else
|
|
113
|
+
while 0 < Atomic.get registering do
|
|
114
|
+
let got = clean_str cl text in
|
|
115
|
+
if got <> masked then failwith ("cleaned to: " ^ got);
|
|
116
|
+
Thread.yield ()
|
|
117
|
+
done) in
|
|
118
|
+
check (Printf.sprintf "round %d raised: %s" round (String.concat "; " raised)) (raised = []);
|
|
119
|
+
every_secret_masked cl round
|
|
120
|
+
done)
|
|
121
|
+
|
|
122
|
+
(* Requests on one client while secrets register on it: each request copies
|
|
123
|
+
* the client's options, the registry among them. *)
|
|
124
|
+
let () = test "concurrent requests survive registration" (fun () ->
|
|
125
|
+
for round = 1 to rounds do
|
|
126
|
+
let cl = live_client () in
|
|
127
|
+
for k = 1 to seeded do
|
|
128
|
+
cl.cl_utility.u_clean_add (root_of cl) (Printf.sprintf "SEEDED-SECRET-%d-%d" round k)
|
|
129
|
+
done;
|
|
130
|
+
let registering = Atomic.make (width / 2) in
|
|
131
|
+
let raised = at_once (fun n ->
|
|
132
|
+
if n < width / 2 then register_secrets cl round n registering
|
|
133
|
+
else
|
|
134
|
+
while 0 < Atomic.get registering do
|
|
135
|
+
let res = Sdk_client.direct cl
|
|
136
|
+
(Sdk_helpers.jo [("path", Str (Printf.sprintf "p%d" n))]) in
|
|
137
|
+
if Sdk_helpers.getp res "ok" <> Bool true then
|
|
138
|
+
failwith ("a request failed: " ^ stringify res);
|
|
139
|
+
Thread.yield ()
|
|
140
|
+
done) in
|
|
141
|
+
check (Printf.sprintf "round %d raised: %s" round (String.concat "; " raised)) (raised = []);
|
|
142
|
+
every_secret_masked cl round
|
|
143
|
+
done)
|
|
144
|
+
|
|
145
|
+
let () =
|
|
146
|
+
List.iter (fun m -> print_endline ("FAIL " ^ m)) (List.rev !failures);
|
|
147
|
+
Printf.printf "concurrency_test: %d passed, %d failed\n" !npass !nfail;
|
|
148
|
+
if !nfail > 0 then exit 1
|
|
@@ -44,7 +44,7 @@ my $field_arg = sub {
|
|
|
44
44
|
|
|
45
45
|
# A header, cookie or query argument travels where prepare_headers or
|
|
46
46
|
# prepare_query sends it, so the body is built from the request data without
|
|
47
|
-
# it, unless the
|
|
47
|
+
# it, unless the point marks it as a field the body keeps.
|
|
48
48
|
my $routed_arg_names = sub {
|
|
49
49
|
my ($ctx) = @_;
|
|
50
50
|
return grep { !$field_arg->($ctx, $_) } map { $_->[0] } ProjectNameUtilities::call_args($ctx, 'header'),
|
|
@@ -392,7 +392,7 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
392
392
|
|
|
393
393
|
/**
|
|
394
394
|
* Build a structured `$AND` query from the request match/data dict,
|
|
395
|
-
* matching the TS test feature's buildArgs
|
|
395
|
+
* matching the TS test feature's buildArgs in ts/src/feature/test/TestFeature.ts.
|
|
396
396
|
*
|
|
397
397
|
* For each key in $args that is 'id' OR a required-param key on the
|
|
398
398
|
* current operation point, emit a `$OR` clause matching the key (and
|
|
@@ -409,12 +409,12 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
409
409
|
}
|
|
410
410
|
|
|
411
411
|
$opname = is_object($op) ? ($op->name ?? null) : (\Voxgig\Struct\Struct::getprop($op, 'name'));
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
412
|
+
|
|
413
|
+
// An entity keeps its name private, behind get_name(); a context
|
|
414
|
+
// with no entity has the name its operation was resolved for.
|
|
415
|
+
$entityName = (is_object($ctx->entity) && method_exists($ctx->entity, 'get_name'))
|
|
416
|
+
? $ctx->entity->get_name()
|
|
417
|
+
: (is_object($op) ? ($op->entity ?? null) : \Voxgig\Struct\Struct::getprop($op, 'entity'));
|
|
418
418
|
|
|
419
419
|
// Resolve required-param names from the op's last point. Defensive:
|
|
420
420
|
// any missing piece falls back to "no required params".
|
|
@@ -427,8 +427,8 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
427
427
|
// `{id}` marks a record route, and failing that the shallower
|
|
428
428
|
// path wins.
|
|
429
429
|
$points = \Voxgig\Struct\Struct::getpath(
|
|
430
|
-
|
|
431
|
-
$
|
|
430
|
+
$ctx->config,
|
|
431
|
+
['entity', $entityName, 'op', $opname, 'points']
|
|
432
432
|
);
|
|
433
433
|
$point = \Voxgig\Struct\Struct::getelem($points, 0);
|
|
434
434
|
if (is_array($points)) {
|
|
@@ -37,7 +37,7 @@ class ProjectNameTransformRequest
|
|
|
37
37
|
|
|
38
38
|
// A header, cookie or query argument travels where PrepareHeaders or
|
|
39
39
|
// PrepareQuery sends it, so the body is built from the request data
|
|
40
|
-
// without it, unless the
|
|
40
|
+
// without it, unless the point marks it as a field the body keeps.
|
|
41
41
|
private static function routed_arg_names(ProjectNameContext $ctx): array
|
|
42
42
|
{
|
|
43
43
|
$args = array_merge(ProjectNameParam::callArgs($ctx, 'header'),
|
|
@@ -202,8 +202,8 @@ class ProjectNameContext:
|
|
|
202
202
|
"points": points,
|
|
203
203
|
})
|
|
204
204
|
|
|
205
|
-
|
|
206
|
-
return op
|
|
205
|
+
# Every request racing to build this Operation gets the one stored first.
|
|
206
|
+
return self.opmap.setdefault(cache_key, op)
|
|
207
207
|
|
|
208
208
|
def make_error(self, code, msg):
|
|
209
209
|
return ProjectNameError(code, msg, self)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
import base64
|
|
5
5
|
import json
|
|
6
|
+
import threading
|
|
6
7
|
from urllib.parse import quote
|
|
7
8
|
|
|
8
9
|
from projectname_sdk.schema import OPTSPEC
|
|
@@ -113,20 +114,23 @@ def _forms(value):
|
|
|
113
114
|
return out
|
|
114
115
|
|
|
115
116
|
|
|
117
|
+
# Requests on other threads clean while one registers, so a registration
|
|
118
|
+
# publishes a new list and never changes a published one.
|
|
119
|
+
_REGISTERING = threading.Lock()
|
|
120
|
+
|
|
121
|
+
|
|
116
122
|
# Register a secret value. Idempotent; shorter than `min` is not a secret
|
|
117
123
|
# the SDK can mask without blanking ordinary text.
|
|
118
124
|
def clean_add_util(ctx, value):
|
|
119
125
|
cfg = _clean_config(ctx)
|
|
120
126
|
if not isinstance(value, str) or len(value) < cfg["min"]:
|
|
121
127
|
return
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if changed:
|
|
129
|
-
values.sort(key=len, reverse=True)
|
|
128
|
+
with _REGISTERING:
|
|
129
|
+
values = cfg["values"]
|
|
130
|
+
added = [form for form in _forms(value)
|
|
131
|
+
if len(form) >= cfg["min"] and form not in values]
|
|
132
|
+
if added:
|
|
133
|
+
cfg["values"] = sorted(values + added, key=len, reverse=True)
|
|
130
134
|
|
|
131
135
|
|
|
132
136
|
# Every scalar under a sensitive name, at any depth and of any shape: a
|
|
@@ -14,7 +14,7 @@ def _strip_action(reqdata):
|
|
|
14
14
|
|
|
15
15
|
# A header, cookie or query argument travels where prepare_headers_util or
|
|
16
16
|
# prepare_query_util sends it, so the body is built from the request data
|
|
17
|
-
# without it, unless the
|
|
17
|
+
# without it, unless the point marks it as a field the body keeps.
|
|
18
18
|
def _routed_arg_names(ctx):
|
|
19
19
|
return [name for name, _orig, _val in
|
|
20
20
|
call_args(ctx, "header") + call_args(ctx, "cookie") + call_args(ctx, "query")
|