@voxgig/sdkgen 4.32.1 → 4.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/voxgig-sdkgen +1 -1
- package/dist/action/copies.d.ts +21 -0
- package/dist/action/copies.js +146 -0
- package/dist/action/copies.js.map +1 -0
- package/dist/action/doctor.d.ts +15 -2
- package/dist/action/doctor.js +169 -23
- package/dist/action/doctor.js.map +1 -1
- package/dist/action/edition.js +6 -2
- package/dist/action/edition.js.map +1 -1
- package/dist/action/feature.js +6 -2
- package/dist/action/feature.js.map +1 -1
- package/dist/action/package.js +28 -10
- package/dist/action/package.js.map +1 -1
- package/dist/action/remove.js +2 -0
- package/dist/action/remove.js.map +1 -1
- package/dist/action/resolve.d.ts +1 -0
- package/dist/action/resolve.js +10 -2
- package/dist/action/resolve.js.map +1 -1
- package/dist/action/target.js +6 -2
- package/dist/action/target.js.map +1 -1
- package/dist/helpers/naming.js +1 -1
- package/dist/helpers/naming.js.map +1 -1
- package/dist/sdkgen.d.ts +2 -1
- package/dist/sdkgen.js +36 -3
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/model/sdkgen.aontu +7 -1
- package/package.json +1 -1
- package/project/.sdk/model/feature/debug.aontu +3 -1
- package/project/.sdk/src/cmp/c/PrepareAuth_c.ts +8 -28
- package/project/.sdk/src/cmp/c/TestClean_c.ts +774 -0
- package/project/.sdk/src/cmp/c/Test_c.ts +3 -0
- package/project/.sdk/src/cmp/c/fragment/Entity.fragment.c +16 -5
- package/project/.sdk/src/cmp/c/fragment/Main.fragment.c +1 -1
- package/project/.sdk/src/cmp/clojure/PrepareAuth_clojure.ts +3 -0
- package/project/.sdk/src/cmp/clojure/TestClean_clojure.ts +473 -0
- package/project/.sdk/src/cmp/clojure/Test_clojure.ts +6 -0
- package/project/.sdk/src/cmp/clojure/fragment/Entity.fragment.clj +18 -8
- package/project/.sdk/src/cmp/cpp/PrepareAuth_cpp.ts +3 -0
- package/project/.sdk/src/cmp/cpp/TestClean_cpp.ts +782 -0
- package/project/.sdk/src/cmp/cpp/TestEntity_cpp.ts +1 -1
- package/project/.sdk/src/cmp/cpp/Test_cpp.ts +2 -0
- package/project/.sdk/src/cmp/csharp/PrepareAuth_csharp.ts +3 -0
- package/project/.sdk/src/cmp/csharp/TestClean_csharp.ts +887 -0
- package/project/.sdk/src/cmp/csharp/Test_csharp.ts +2 -0
- package/project/.sdk/src/cmp/csharp/fragment/EntityBase.fragment.cs +68 -4
- package/project/.sdk/src/cmp/csharp/fragment/Main.fragment.cs +8 -2
- package/project/.sdk/src/cmp/csharp/fragment/SdkError.fragment.cs +49 -0
- package/project/.sdk/src/cmp/elixir/PrepareAuth_elixir.ts +3 -0
- package/project/.sdk/src/cmp/elixir/Schema_elixir.ts +20 -10
- package/project/.sdk/src/cmp/elixir/TestClean_elixir.ts +666 -0
- package/project/.sdk/src/cmp/elixir/Test_elixir.ts +4 -0
- package/project/.sdk/src/cmp/elixir/fragment/Main.fragment.ex +1 -1
- package/project/.sdk/src/cmp/elixir/fragment/Pipeline.fragment.ex +14 -2
- package/project/.sdk/src/cmp/go/PrepareAuth_go.ts +3 -0
- package/project/.sdk/src/cmp/go/TestClean_go.ts +804 -0
- package/project/.sdk/src/cmp/go/Test_go.ts +3 -0
- package/project/.sdk/src/cmp/go/fragment/Entity.fragment.go +54 -4
- package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +27 -2
- package/project/.sdk/src/cmp/java/PrepareAuth_java.ts +3 -0
- package/project/.sdk/src/cmp/java/TestClean_java.ts +884 -0
- package/project/.sdk/src/cmp/java/Test_java.ts +2 -0
- package/project/.sdk/src/cmp/java/fragment/EntityBase.fragment.java +56 -11
- package/project/.sdk/src/cmp/java/fragment/SdkError.fragment.java +36 -3
- package/project/.sdk/src/cmp/js/PrepareAuth_js.ts +3 -0
- package/project/.sdk/src/cmp/js/TestClean_js.ts +458 -0
- package/project/.sdk/src/cmp/js/Test_js.ts +2 -0
- package/project/.sdk/src/cmp/js/fragment/EntityBase.fragment.js +5 -4
- package/project/.sdk/src/cmp/js/fragment/EntityCreateOp.fragment.js +7 -1
- package/project/.sdk/src/cmp/js/fragment/EntityListOp.fragment.js +7 -1
- package/project/.sdk/src/cmp/js/fragment/EntityLoadOp.fragment.js +7 -1
- package/project/.sdk/src/cmp/js/fragment/EntityRemoveOp.fragment.js +7 -1
- package/project/.sdk/src/cmp/js/fragment/EntityUpdateOp.fragment.js +7 -1
- package/project/.sdk/src/cmp/js/fragment/Main.fragment.js +11 -2
- package/project/.sdk/src/cmp/js/fragment/SdkError.fragment.js +17 -1
- package/project/.sdk/src/cmp/kotlin/Package_kotlin.ts +5 -0
- package/project/.sdk/src/cmp/kotlin/PrepareAuth_kotlin.ts +3 -0
- package/project/.sdk/src/cmp/kotlin/TestClean_kotlin.ts +654 -0
- package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +3 -0
- package/project/.sdk/src/cmp/kotlin/fragment/EntityBase.fragment.kt +44 -11
- package/project/.sdk/src/cmp/kotlin/fragment/SdkError.fragment.kt +31 -0
- package/project/.sdk/src/cmp/lua/PrepareAuth_lua.ts +3 -0
- package/project/.sdk/src/cmp/lua/TestClean_lua.ts +769 -0
- package/project/.sdk/src/cmp/lua/Test_lua.ts +3 -0
- package/project/.sdk/src/cmp/lua/fragment/Entity.fragment.lua +77 -1
- package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +49 -2
- package/project/.sdk/src/cmp/ocaml/Entity_ocaml.ts +3 -2
- package/project/.sdk/src/cmp/ocaml/PrepareAuth_ocaml.ts +4 -25
- package/project/.sdk/src/cmp/ocaml/TestClean_ocaml.ts +538 -0
- package/project/.sdk/src/cmp/ocaml/Test_ocaml.ts +5 -0
- package/project/.sdk/src/cmp/ocaml/fragment/RunOp.fragment.ml +16 -4
- package/project/.sdk/src/cmp/perl/PrepareAuth_perl.ts +3 -0
- package/project/.sdk/src/cmp/perl/TestClean_perl.ts +619 -0
- package/project/.sdk/src/cmp/perl/Test_perl.ts +3 -0
- package/project/.sdk/src/cmp/perl/fragment/Entity.fragment.pm +67 -5
- package/project/.sdk/src/cmp/perl/fragment/Main.fragment.pm +13 -1
- package/project/.sdk/src/cmp/php/PrepareAuth_php.ts +3 -0
- package/project/.sdk/src/cmp/php/TestClean_php.ts +809 -0
- package/project/.sdk/src/cmp/php/Test_php.ts +2 -0
- package/project/.sdk/src/cmp/php/fragment/Entity.fragment.php +96 -55
- package/project/.sdk/src/cmp/php/fragment/Main.fragment.php +15 -2
- package/project/.sdk/src/cmp/py/PrepareAuth_py.ts +3 -0
- package/project/.sdk/src/cmp/py/TestClean_py.ts +549 -0
- package/project/.sdk/src/cmp/py/Test_py.ts +3 -0
- package/project/.sdk/src/cmp/py/fragment/Entity.fragment.py +83 -55
- package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +19 -3
- package/project/.sdk/src/cmp/rb/PrepareAuth_rb.ts +3 -0
- package/project/.sdk/src/cmp/rb/TestClean_rb.ts +535 -0
- package/project/.sdk/src/cmp/rb/Test_rb.ts +3 -0
- package/project/.sdk/src/cmp/rb/fragment/Entity.fragment.rb +108 -46
- package/project/.sdk/src/cmp/rb/fragment/Main.fragment.rb +12 -1
- package/project/.sdk/src/cmp/rust/PrepareAuth_rust.ts +9 -45
- package/project/.sdk/src/cmp/rust/TestClean_rust.ts +881 -0
- package/project/.sdk/src/cmp/rust/Test_rust.ts +3 -0
- package/project/.sdk/src/cmp/rust/fragment/Entity.fragment.rs +14 -6
- package/project/.sdk/src/cmp/rust/fragment/Main.fragment.rs +17 -1
- package/project/.sdk/src/cmp/rust/fragment/SdkError.fragment.rs +34 -2
- package/project/.sdk/src/cmp/scala/PrepareAuth_scala.ts +3 -0
- package/project/.sdk/src/cmp/scala/TestClean_scala.ts +608 -0
- package/project/.sdk/src/cmp/scala/Test_scala.ts +3 -0
- package/project/.sdk/src/cmp/scala/fragment/EntityBase.fragment.scala +58 -6
- package/project/.sdk/src/cmp/swift/PrepareAuth_swift.ts +3 -0
- package/project/.sdk/src/cmp/swift/TestClean_swift.ts +671 -0
- package/project/.sdk/src/cmp/swift/Test_swift.ts +3 -0
- package/project/.sdk/src/cmp/swift/fragment/EntityBase.fragment.swift +10 -0
- package/project/.sdk/src/cmp/swift/fragment/Main.fragment.swift +16 -2
- package/project/.sdk/src/cmp/swift/fragment/SdkError.fragment.swift +47 -3
- package/project/.sdk/src/cmp/ts/PrepareAuth_ts.ts +3 -0
- package/project/.sdk/src/cmp/ts/TestClean_ts.ts +465 -0
- package/project/.sdk/src/cmp/ts/Test_ts.ts +2 -0
- package/project/.sdk/src/cmp/ts/fragment/EntityBase.fragment.ts +5 -4
- package/project/.sdk/src/cmp/ts/fragment/EntityCreateOp.fragment.ts +7 -1
- package/project/.sdk/src/cmp/ts/fragment/EntityListOp.fragment.ts +7 -1
- package/project/.sdk/src/cmp/ts/fragment/EntityLoadOp.fragment.ts +7 -1
- package/project/.sdk/src/cmp/ts/fragment/EntityRemoveOp.fragment.ts +7 -1
- package/project/.sdk/src/cmp/ts/fragment/EntityUpdateOp.fragment.ts +7 -1
- package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +8 -2
- package/project/.sdk/src/cmp/ts/fragment/SdkError.fragment.ts +20 -2
- package/project/.sdk/src/cmp/zig/PrepareAuth_zig.ts +3 -0
- package/project/.sdk/src/cmp/zig/TestClean_zig.ts +791 -0
- package/project/.sdk/src/cmp/zig/Test_zig.ts +5 -0
- package/project/.sdk/src/cmp/zig/fragment/Build.fragment.zig +19 -0
- package/project/.sdk/src/cmp/zig/fragment/Entity.fragment.zig +14 -5
- package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +1 -1
- package/project/.sdk/tm/c/core/context.c +25 -0
- package/project/.sdk/tm/c/core/error.c +16 -0
- package/project/.sdk/tm/c/core/sdk.h +22 -0
- package/project/.sdk/tm/c/feature/audit.c +2 -0
- package/project/.sdk/tm/c/feature/cost.c +1 -0
- package/project/.sdk/tm/c/feature/debug.c +19 -29
- package/project/.sdk/tm/c/feature/log.c +21 -2
- package/project/.sdk/tm/c/feature/proxy.c +55 -3
- package/project/.sdk/tm/c/feature/secrets.c +7 -0
- package/project/.sdk/tm/c/feature/telemetry.c +2 -0
- package/project/.sdk/tm/c/tests/feature_test.c +2 -2
- package/project/.sdk/tm/c/utility/clean.c +530 -8
- package/project/.sdk/tm/c/utility/done.c +28 -12
- package/project/.sdk/tm/c/utility/make_error.c +4 -3
- package/project/.sdk/tm/c/utility/make_options.c +71 -45
- package/project/.sdk/tm/clojure/feature/secrets/sdk/feature/secrets.clj +15 -0
- package/project/.sdk/tm/clojure/src/sdk/client.clj +20 -4
- package/project/.sdk/tm/clojure/src/sdk/core.clj +319 -35
- package/project/.sdk/tm/clojure/src/sdk/features.clj +68 -30
- package/project/.sdk/tm/clojure/test/sdk/test/feature.clj +4 -2
- package/project/.sdk/tm/clojure/test/sdk/test/pipeline.clj +47 -20
- package/project/.sdk/tm/clojure/test/sdk/test_runner.clj +4 -0
- package/project/.sdk/tm/cpp/core/types.hpp +93 -3
- package/project/.sdk/tm/cpp/feature/audit.hpp +1 -0
- package/project/.sdk/tm/cpp/feature/cost.hpp +1 -0
- package/project/.sdk/tm/cpp/feature/debug.hpp +20 -12
- package/project/.sdk/tm/cpp/feature/log.hpp +31 -9
- package/project/.sdk/tm/cpp/feature/proxy.hpp +52 -6
- package/project/.sdk/tm/cpp/feature/secrets.hpp +13 -0
- package/project/.sdk/tm/cpp/feature/telemetry.hpp +4 -2
- package/project/.sdk/tm/cpp/test/feature_test.cpp +2 -2
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +426 -51
- package/project/.sdk/tm/csharp/core/Context.cs +32 -0
- package/project/.sdk/tm/csharp/core/Utility.cs +2 -0
- package/project/.sdk/tm/csharp/feature/AuditFeature.cs +1 -0
- package/project/.sdk/tm/csharp/feature/CostFeature.cs +7 -5
- package/project/.sdk/tm/csharp/feature/DebugFeature.cs +19 -16
- package/project/.sdk/tm/csharp/feature/FeatureOptions.cs +16 -0
- package/project/.sdk/tm/csharp/feature/LogFeature.cs +4 -1
- package/project/.sdk/tm/csharp/feature/ProxyFeature.cs +26 -5
- package/project/.sdk/tm/csharp/feature/SecretsFeature.cs +16 -0
- package/project/.sdk/tm/csharp/feature/TelemetryFeature.cs +4 -2
- package/project/.sdk/tm/csharp/test/FeatureTest.cs +2 -2
- package/project/.sdk/tm/csharp/utility/Clean.cs +440 -2
- package/project/.sdk/tm/csharp/utility/Done.cs +27 -11
- package/project/.sdk/tm/csharp/utility/MakeError.cs +19 -8
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +119 -29
- package/project/.sdk/tm/csharp/utility/MakeResponse.cs +21 -11
- package/project/.sdk/tm/csharp/utility/Register.cs +1 -0
- package/project/.sdk/tm/elixir/lib/projectname/context.ex +19 -0
- package/project/.sdk/tm/elixir/lib/projectname/entity_base.ex +22 -1
- package/project/.sdk/tm/elixir/lib/projectname/error.ex +24 -0
- package/project/.sdk/tm/elixir/lib/projectname/feature/audit.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/cost.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/debug.ex +13 -13
- package/project/.sdk/tm/elixir/lib/projectname/feature/log.ex +18 -5
- package/project/.sdk/tm/elixir/lib/projectname/feature/proxy.ex +19 -1
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets.ex +15 -0
- package/project/.sdk/tm/elixir/lib/projectname/feature/telemetry.ex +4 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature.ex +22 -0
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +464 -41
- package/project/.sdk/tm/elixir/test/feature_test.exs +3 -2
- package/project/.sdk/tm/go/core/context.go +42 -0
- package/project/.sdk/tm/go/core/error.go +30 -10
- package/project/.sdk/tm/go/core/utility_type.go +2 -0
- package/project/.sdk/tm/go/feature/audit_feature.go +1 -0
- package/project/.sdk/tm/go/feature/base_feature.go +12 -0
- package/project/.sdk/tm/go/feature/cost_feature.go +2 -2
- package/project/.sdk/tm/go/feature/debug_feature.go +14 -12
- package/project/.sdk/tm/go/feature/log_feature.go +13 -8
- package/project/.sdk/tm/go/feature/proxy_feature.go +26 -6
- package/project/.sdk/tm/go/feature/secrets_feature.go +19 -0
- package/project/.sdk/tm/go/feature/telemetry_feature.go +4 -2
- package/project/.sdk/tm/go/test/feature_test.go +2 -2
- package/project/.sdk/tm/go/utility/clean.go +585 -2
- package/project/.sdk/tm/go/utility/done.go +29 -9
- package/project/.sdk/tm/go/utility/make_error.go +51 -16
- package/project/.sdk/tm/go/utility/make_options.go +77 -36
- package/project/.sdk/tm/go/utility/register.go +12 -0
- package/project/.sdk/tm/java/core/Context.java +29 -0
- package/project/.sdk/tm/java/core/SdkClient.java +4 -2
- package/project/.sdk/tm/java/core/Utility.java +7 -0
- package/project/.sdk/tm/java/feature/AuditFeature.java +6 -0
- package/project/.sdk/tm/java/feature/CostFeature.java +5 -0
- package/project/.sdk/tm/java/feature/DebugFeature.java +25 -14
- package/project/.sdk/tm/java/feature/LogFeature.java +11 -4
- package/project/.sdk/tm/java/feature/ProxyFeature.java +41 -6
- package/project/.sdk/tm/java/feature/SecretsFeature.java +18 -0
- package/project/.sdk/tm/java/feature/TelemetryFeature.java +8 -2
- package/project/.sdk/tm/java/test/FeatureTest.java +2 -2
- package/project/.sdk/tm/java/utility/Clean.java +519 -1
- package/project/.sdk/tm/java/utility/Done.java +1 -1
- package/project/.sdk/tm/java/utility/MakeError.java +5 -1
- package/project/.sdk/tm/java/utility/MakeOptions.java +55 -31
- package/project/.sdk/tm/java/utility/Register.java +4 -0
- package/project/.sdk/tm/js/src/Context.js +5 -1
- package/project/.sdk/tm/js/src/feature/audit/AuditFeature.js +2 -2
- package/project/.sdk/tm/js/src/feature/cost/CostFeature.js +2 -2
- package/project/.sdk/tm/js/src/feature/debug/DebugFeature.js +13 -7
- package/project/.sdk/tm/js/src/feature/log/LogFeature.js +9 -5
- package/project/.sdk/tm/js/src/feature/proxy/ProxyFeature.js +19 -1
- package/project/.sdk/tm/js/src/feature/secrets/SecretsFeature.js +14 -0
- package/project/.sdk/tm/js/src/feature/telemetry/TelemetryFeature.js +4 -2
- package/project/.sdk/tm/js/src/utility/CleanUtility.js +294 -21
- package/project/.sdk/tm/js/src/utility/DoneUtility.js +17 -5
- package/project/.sdk/tm/js/src/utility/MakeErrorUtility.js +18 -2
- package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +39 -15
- package/project/.sdk/tm/js/src/utility/Utility.js +4 -2
- package/project/.sdk/tm/js/test/feature/harness.js +11 -1
- package/project/.sdk/tm/js/test/feature.test.js +9 -2
- package/project/.sdk/tm/kotlin/core/Context.kt +19 -0
- package/project/.sdk/tm/kotlin/core/SdkClient.kt +9 -2
- package/project/.sdk/tm/kotlin/core/Utility.kt +2 -0
- package/project/.sdk/tm/kotlin/feature/AuditFeature.kt +11 -9
- package/project/.sdk/tm/kotlin/feature/CostFeature.kt +8 -5
- package/project/.sdk/tm/kotlin/feature/DebugFeature.kt +15 -22
- package/project/.sdk/tm/kotlin/feature/LogFeature.kt +8 -4
- package/project/.sdk/tm/kotlin/feature/ProxyFeature.kt +26 -2
- package/project/.sdk/tm/kotlin/feature/SecretsFeature.kt +11 -0
- package/project/.sdk/tm/kotlin/feature/TelemetryFeature.kt +4 -2
- package/project/.sdk/tm/kotlin/test/FeatureTest.kt +2 -2
- package/project/.sdk/tm/kotlin/utility/CoreUtil.kt +447 -11
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +38 -31
- package/project/.sdk/tm/kotlin/utility/Register.kt +1 -0
- package/project/.sdk/tm/lua/core/context.lua +43 -0
- package/project/.sdk/tm/lua/core/error.lua +36 -1
- package/project/.sdk/tm/lua/core/utility_type.lua +4 -0
- package/project/.sdk/tm/lua/feature/audit_feature.lua +2 -2
- package/project/.sdk/tm/lua/feature/cost_feature.lua +2 -2
- package/project/.sdk/tm/lua/feature/debug_feature.lua +21 -17
- package/project/.sdk/tm/lua/feature/log_feature.lua +2 -1
- package/project/.sdk/tm/lua/feature/proxy_feature.lua +23 -1
- package/project/.sdk/tm/lua/feature/secrets_feature.lua +18 -0
- package/project/.sdk/tm/lua/feature/telemetry_feature.lua +4 -2
- package/project/.sdk/tm/lua/test/feature/secrets/secrets_feature_test.lua +3 -2
- package/project/.sdk/tm/lua/test/feature_test.lua +10 -3
- package/project/.sdk/tm/lua/utility/clean.lua +426 -2
- package/project/.sdk/tm/lua/utility/clean_explain.lua +34 -0
- package/project/.sdk/tm/lua/utility/done.lua +1 -7
- package/project/.sdk/tm/lua/utility/make_error.lua +3 -1
- package/project/.sdk/tm/lua/utility/make_options.lua +76 -30
- package/project/.sdk/tm/lua/utility/register.lua +4 -1
- package/project/.sdk/tm/ocaml/Makefile +5 -1
- package/project/.sdk/tm/ocaml/feature/secrets_feature.ml +12 -1
- package/project/.sdk/tm/ocaml/sdk_features.ml +78 -18
- package/project/.sdk/tm/ocaml/sdk_helpers.ml +27 -0
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +304 -26
- package/project/.sdk/tm/ocaml/sdk_types.ml +1 -0
- package/project/.sdk/tm/ocaml/test/feature/secrets/t_secrets.ml +3 -1
- package/project/.sdk/tm/ocaml/test/t_feature.ml +2 -2
- package/project/.sdk/tm/perl/core/context.pm +27 -0
- package/project/.sdk/tm/perl/core/error.pm +52 -5
- package/project/.sdk/tm/perl/feature/audit_feature.pm +2 -2
- package/project/.sdk/tm/perl/feature/cost_feature.pm +2 -2
- package/project/.sdk/tm/perl/feature/debug_feature.pm +17 -13
- package/project/.sdk/tm/perl/feature/log_feature.pm +3 -1
- package/project/.sdk/tm/perl/feature/proxy_feature.pm +21 -1
- package/project/.sdk/tm/perl/feature/secrets_feature.pm +20 -1
- package/project/.sdk/tm/perl/feature/telemetry_feature.pm +4 -2
- package/project/.sdk/tm/perl/t/feature.t +4 -2
- package/project/.sdk/tm/perl/utility/clean.pm +345 -1
- package/project/.sdk/tm/perl/utility/done.pm +24 -6
- package/project/.sdk/tm/perl/utility/make_error.pm +17 -4
- package/project/.sdk/tm/perl/utility/make_options.pm +33 -14
- package/project/.sdk/tm/php/core/Context.php +24 -1
- package/project/.sdk/tm/php/core/Error.php +34 -3
- package/project/.sdk/tm/php/core/UtilityType.php +4 -0
- package/project/.sdk/tm/php/feature/AuditFeature.php +2 -2
- package/project/.sdk/tm/php/feature/CostFeature.php +2 -2
- package/project/.sdk/tm/php/feature/DebugFeature.php +18 -9
- package/project/.sdk/tm/php/feature/LogFeature.php +5 -2
- package/project/.sdk/tm/php/feature/ProxyFeature.php +20 -1
- package/project/.sdk/tm/php/feature/SecretsFeature.php +18 -0
- package/project/.sdk/tm/php/feature/TelemetryFeature.php +4 -2
- package/project/.sdk/tm/php/test/FeatureTest.php +2 -2
- package/project/.sdk/tm/php/utility/Clean.php +438 -2
- package/project/.sdk/tm/php/utility/Done.php +14 -7
- package/project/.sdk/tm/php/utility/MakeError.php +9 -7
- package/project/.sdk/tm/php/utility/MakeOptions.php +62 -38
- package/project/.sdk/tm/php/utility/Register.php +2 -0
- package/project/.sdk/tm/py/pkg/core/context.py +17 -0
- package/project/.sdk/tm/py/pkg/core/error.py +42 -1
- package/project/.sdk/tm/py/pkg/core/utility_type.py +4 -0
- package/project/.sdk/tm/py/pkg/feature/audit_feature.py +2 -2
- package/project/.sdk/tm/py/pkg/feature/cost_feature.py +2 -2
- package/project/.sdk/tm/py/pkg/feature/debug_feature.py +17 -12
- package/project/.sdk/tm/py/pkg/feature/log_feature.py +2 -1
- package/project/.sdk/tm/py/pkg/feature/proxy_feature.py +18 -1
- package/project/.sdk/tm/py/pkg/feature/secrets_feature.py +12 -0
- package/project/.sdk/tm/py/pkg/feature/telemetry_feature.py +4 -2
- package/project/.sdk/tm/py/pkg/utility/clean.py +311 -1
- package/project/.sdk/tm/py/pkg/utility/done.py +18 -5
- package/project/.sdk/tm/py/pkg/utility/make_error.py +9 -6
- package/project/.sdk/tm/py/pkg/utility/make_options.py +64 -25
- package/project/.sdk/tm/py/pkg/utility/make_response.py +19 -13
- package/project/.sdk/tm/py/pkg/utility/register.py +4 -2
- package/project/.sdk/tm/py/test/feature_harness.py +4 -0
- package/project/.sdk/tm/py/test/test_feature.py +2 -2
- package/project/.sdk/tm/py/test/test_primary_utility.py +1 -0
- package/project/.sdk/tm/rb/core/context.rb +28 -0
- package/project/.sdk/tm/rb/core/error.rb +30 -0
- package/project/.sdk/tm/rb/core/utility_type.rb +1 -1
- package/project/.sdk/tm/rb/feature/audit_feature.rb +2 -2
- package/project/.sdk/tm/rb/feature/cost_feature.rb +2 -2
- package/project/.sdk/tm/rb/feature/debug_feature.rb +15 -12
- package/project/.sdk/tm/rb/feature/log_feature.rb +3 -1
- package/project/.sdk/tm/rb/feature/proxy_feature.rb +25 -1
- package/project/.sdk/tm/rb/feature/secrets_feature.rb +17 -0
- package/project/.sdk/tm/rb/feature/telemetry_feature.rb +4 -2
- package/project/.sdk/tm/rb/test/feature_test.rb +4 -2
- package/project/.sdk/tm/rb/utility/clean.rb +312 -1
- package/project/.sdk/tm/rb/utility/done.rb +15 -5
- package/project/.sdk/tm/rb/utility/make_error.rb +13 -4
- package/project/.sdk/tm/rb/utility/make_options.rb +31 -12
- package/project/.sdk/tm/rb/utility/register.rb +2 -0
- package/project/.sdk/tm/rust/core/context.rs +55 -0
- package/project/.sdk/tm/rust/core/utility_type.rs +17 -0
- package/project/.sdk/tm/rust/feature/audit.rs +2 -0
- package/project/.sdk/tm/rust/feature/cost.rs +3 -2
- package/project/.sdk/tm/rust/feature/debug.rs +16 -16
- package/project/.sdk/tm/rust/feature/log.rs +42 -7
- package/project/.sdk/tm/rust/feature/proxy.rs +48 -2
- package/project/.sdk/tm/rust/feature/secrets.rs +12 -0
- package/project/.sdk/tm/rust/feature/telemetry.rs +2 -0
- package/project/.sdk/tm/rust/tests/feature_test.rs +2 -2
- package/project/.sdk/tm/rust/utility/clean.rs +446 -5
- package/project/.sdk/tm/rust/utility/done.rs +22 -12
- package/project/.sdk/tm/rust/utility/make_error.rs +4 -1
- package/project/.sdk/tm/rust/utility/make_options.rs +73 -19
- package/project/.sdk/tm/scala/Makefile +3 -1
- package/project/.sdk/tm/scala/core/Context.scala +16 -0
- package/project/.sdk/tm/scala/core/SdkClient.scala +14 -4
- package/project/.sdk/tm/scala/core/SdkError.scala +28 -0
- package/project/.sdk/tm/scala/core/Types.scala +3 -0
- package/project/.sdk/tm/scala/core/Utility.scala +2 -0
- package/project/.sdk/tm/scala/feature/AuditFeature.scala +16 -10
- package/project/.sdk/tm/scala/feature/CostFeature.scala +10 -5
- package/project/.sdk/tm/scala/feature/DebugFeature.scala +24 -21
- package/project/.sdk/tm/scala/feature/LogFeature.scala +9 -4
- package/project/.sdk/tm/scala/feature/ProxyFeature.scala +23 -2
- package/project/.sdk/tm/scala/feature/SecretsFeature.scala +14 -0
- package/project/.sdk/tm/scala/feature/TelemetryFeature.scala +9 -2
- package/project/.sdk/tm/scala/sdktest/SdkTestMain.scala +2 -2
- package/project/.sdk/tm/scala/utility/Make.scala +63 -20
- package/project/.sdk/tm/scala/utility/Misc.scala +358 -5
- package/project/.sdk/tm/scala/utility/Register.scala +1 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Context.swift +29 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Utility.swift +2 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/AuditFeature.swift +10 -9
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/CostFeature.swift +7 -5
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/DebugFeature.swift +19 -15
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/FeatureOptions.swift +16 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/LogFeature.swift +4 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/ProxyFeature.swift +20 -5
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/SecretsFeature.swift +13 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/TelemetryFeature.swift +4 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeError.swift +15 -5
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +58 -19
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Register.swift +1 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/ResultUtil.swift +391 -4
- package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/FeatureTest.swift +2 -2
- package/project/.sdk/tm/ts/src/Context.ts +5 -1
- package/project/.sdk/tm/ts/src/feature/audit/AuditFeature.ts +2 -2
- package/project/.sdk/tm/ts/src/feature/cost/CostFeature.ts +2 -2
- package/project/.sdk/tm/ts/src/feature/debug/DebugFeature.ts +13 -7
- package/project/.sdk/tm/ts/src/feature/log/LogFeature.ts +9 -5
- package/project/.sdk/tm/ts/src/feature/proxy/ProxyFeature.ts +19 -1
- package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +12 -0
- package/project/.sdk/tm/ts/src/feature/telemetry/TelemetryFeature.ts +4 -2
- package/project/.sdk/tm/ts/src/utility/CleanUtility.ts +287 -9
- package/project/.sdk/tm/ts/src/utility/DoneUtility.ts +18 -5
- package/project/.sdk/tm/ts/src/utility/MakeErrorUtility.ts +18 -8
- package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +38 -16
- package/project/.sdk/tm/ts/src/utility/Utility.ts +4 -2
- package/project/.sdk/tm/ts/test/feature/harness.ts +11 -1
- package/project/.sdk/tm/ts/test/feature.test.ts +9 -2
- package/project/.sdk/tm/zig/core/context.zig +34 -0
- package/project/.sdk/tm/zig/core/error.zig +22 -1
- package/project/.sdk/tm/zig/core/utility.zig +403 -39
- package/project/.sdk/tm/zig/feature/audit.zig +4 -2
- package/project/.sdk/tm/zig/feature/cost.zig +3 -2
- package/project/.sdk/tm/zig/feature/debug.zig +20 -25
- package/project/.sdk/tm/zig/feature/log.zig +3 -1
- package/project/.sdk/tm/zig/feature/proxy.zig +47 -6
- package/project/.sdk/tm/zig/feature/secrets.zig +7 -0
- package/project/.sdk/tm/zig/feature/telemetry.zig +4 -2
- package/project/.sdk/tm/zig/test/feature_test.zig +2 -2
- package/project/sdkgen-package.json +1 -1
- package/src/action/copies.ts +219 -0
- package/src/action/doctor.ts +247 -21
- package/src/action/edition.ts +10 -2
- package/src/action/feature.ts +11 -2
- package/src/action/package.ts +31 -10
- package/src/action/remove.ts +4 -0
- package/src/action/resolve.ts +16 -3
- package/src/action/target.ts +10 -2
- package/src/helpers/naming.ts +1 -1
- package/src/sdkgen.ts +45 -5
|
@@ -0,0 +1,887 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cmp,
|
|
3
|
+
each,
|
|
4
|
+
entityCollection,
|
|
5
|
+
File,
|
|
6
|
+
Content,
|
|
7
|
+
isAuthSuppressed,
|
|
8
|
+
isHttpBasicAuth,
|
|
9
|
+
resolveAuthIn,
|
|
10
|
+
resolveAuthName,
|
|
11
|
+
} from '@voxgig/sdkgen'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
// The canary sweep, the csharp twin of TestClean_ts.ts. The entity accessors
|
|
15
|
+
// and their operations are typed methods, so the candidates the sweep drives
|
|
16
|
+
// to find a usable operation are emitted from the model rather than found by
|
|
17
|
+
// walking the client at run time.
|
|
18
|
+
const TestClean = cmp(function TestClean(props: any) {
|
|
19
|
+
const { model } = props.ctx$
|
|
20
|
+
const { target } = props
|
|
21
|
+
|
|
22
|
+
const auth = {
|
|
23
|
+
suppressed: isAuthSuppressed(model),
|
|
24
|
+
where: resolveAuthIn(model),
|
|
25
|
+
name: 'header' === resolveAuthIn(model)
|
|
26
|
+
? resolveAuthName(model).toLowerCase() : resolveAuthName(model),
|
|
27
|
+
basic: isHttpBasicAuth(model),
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Same order the ts sweep tries: list, then load, then the rest.
|
|
31
|
+
const rank: Record<string, number> = { list: 0, load: 1 }
|
|
32
|
+
const candidates = each(entityCollection(model))
|
|
33
|
+
.filter((e: any) => false !== e.active)
|
|
34
|
+
.map((e: any) => ({
|
|
35
|
+
name: e.name,
|
|
36
|
+
Name: e.Name,
|
|
37
|
+
ops: Object.keys(e.op || {})
|
|
38
|
+
.sort((a, b) => (rank[a] ?? 2) - (rank[b] ?? 2)),
|
|
39
|
+
}))
|
|
40
|
+
.filter((c: any) => 0 < c.ops.length)
|
|
41
|
+
|
|
42
|
+
File({ name: 'CleanTest.' + target.ext }, () => Content(render(model.const.Name, auth, candidates)))
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
function render(
|
|
47
|
+
Name: string,
|
|
48
|
+
auth: { suppressed: boolean, where: string, name: string, basic: boolean },
|
|
49
|
+
candidates: { name: string, Name: string, ops: string[] }[],
|
|
50
|
+
): string {
|
|
51
|
+
const candidateLines = candidates.map((c) =>
|
|
52
|
+
` new Candidate("${c.name}", sdk => sdk.${c.Name}(null), new[] { ${c.ops.map((o) => `"${o}"`).join(', ')} }),`)
|
|
53
|
+
.join('\n')
|
|
54
|
+
|
|
55
|
+
return `// The canary sweep: every credential slot holds a distinctive value, every
|
|
56
|
+
// diagnostic feature this SDK ships is switched on with a capturing sink, a
|
|
57
|
+
// real operation runs through every outcome, and every string that leaves
|
|
58
|
+
// the SDK is searched for the canaries and their encoded forms. It also
|
|
59
|
+
// proves its own sensitivity: with clean switched off the canary MUST show.
|
|
60
|
+
|
|
61
|
+
using System.Collections;
|
|
62
|
+
using System.Text;
|
|
63
|
+
using System.Text.Json;
|
|
64
|
+
using System.Text.Json.Serialization;
|
|
65
|
+
|
|
66
|
+
using Voxgig.Struct;
|
|
67
|
+
using Xunit;
|
|
68
|
+
using Xunit.Abstractions;
|
|
69
|
+
|
|
70
|
+
using ${Name}Sdk;
|
|
71
|
+
using ${Name}Sdk.Feature;
|
|
72
|
+
|
|
73
|
+
namespace ${Name}Sdk.Test;
|
|
74
|
+
|
|
75
|
+
public class CleanTest
|
|
76
|
+
{
|
|
77
|
+
// Generated: the credential's wire placement is fixed when the SDK is built.
|
|
78
|
+
private const bool AuthSuppressed = ${auth.suppressed ? 'true' : 'false'};
|
|
79
|
+
private const string AuthWhere = ${JSON.stringify(auth.where)};
|
|
80
|
+
private const string AuthName = ${JSON.stringify(auth.name)};
|
|
81
|
+
|
|
82
|
+
private const string CanaryApikey = "CANARY-APIKEY-k9x2m7q4p1";
|
|
83
|
+
private const string CanarySecret = "CANARY-SECRET-w3e8r5t2y6";
|
|
84
|
+
private const string CanaryHeader = "CANARY-HEADER-z1x4c7v0b3";
|
|
85
|
+
private const string CanaryValue = "CANARY-VALUE-n5m8b2v9c4";
|
|
86
|
+
|
|
87
|
+
private const string Mask = "[redacted]";
|
|
88
|
+
|
|
89
|
+
private readonly ITestOutputHelper _out;
|
|
90
|
+
|
|
91
|
+
public CleanTest(ITestOutputHelper output)
|
|
92
|
+
{
|
|
93
|
+
_out = output;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Every form a canary can travel in.
|
|
97
|
+
private static readonly List<string> Forms = BuildForms();
|
|
98
|
+
|
|
99
|
+
private static List<string> BuildForms()
|
|
100
|
+
{
|
|
101
|
+
var forms = new List<string>();
|
|
102
|
+
foreach (var v in new[] { CanaryApikey, CanarySecret, CanaryHeader, CanaryValue })
|
|
103
|
+
{
|
|
104
|
+
forms.Add(v);
|
|
105
|
+
forms.Add(Convert.ToBase64String(Encoding.UTF8.GetBytes(v)));
|
|
106
|
+
forms.Add(Uri.EscapeDataString(v));
|
|
107
|
+
}
|
|
108
|
+
forms.Add(Convert.ToBase64String(Encoding.UTF8.GetBytes(CanaryApikey + ":" + CanarySecret)));
|
|
109
|
+
return forms;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private sealed record Sink(string Name, string Text);
|
|
113
|
+
|
|
114
|
+
private sealed record Candidate(string Name, Func<${Name}SDK, ${Name}EntityBase> Accessor, string[] Ops);
|
|
115
|
+
|
|
116
|
+
private sealed record Target(Candidate Candidate, string Op, Dictionary<string, object?> Match);
|
|
117
|
+
|
|
118
|
+
// Header maps keep the caller's spelling; the assertion should not care.
|
|
119
|
+
private static object? Header(object? map, string name)
|
|
120
|
+
{
|
|
121
|
+
if (map is IDictionary dict)
|
|
122
|
+
{
|
|
123
|
+
foreach (DictionaryEntry kv in dict)
|
|
124
|
+
{
|
|
125
|
+
if (string.Equals(Convert.ToString(kv.Key), name, StringComparison.OrdinalIgnoreCase))
|
|
126
|
+
{
|
|
127
|
+
return kv.Value;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private static List<string> Leaks(string text)
|
|
135
|
+
{
|
|
136
|
+
return Forms.Where(f => text.Contains(f)).ToList();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private static readonly JsonSerializerOptions FieldsJson = new()
|
|
140
|
+
{
|
|
141
|
+
IncludeFields = true,
|
|
142
|
+
ReferenceHandler = ReferenceHandler.IgnoreCycles,
|
|
143
|
+
MaxDepth = 16,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// The typed pipeline products read through their fields, so the raw spec
|
|
147
|
+
// is visible when clean is off - the sensitivity check depends on it.
|
|
148
|
+
private static string Render(object? val)
|
|
149
|
+
{
|
|
150
|
+
switch (val)
|
|
151
|
+
{
|
|
152
|
+
case null:
|
|
153
|
+
return "null";
|
|
154
|
+
case string s:
|
|
155
|
+
return s;
|
|
156
|
+
case Spec sp:
|
|
157
|
+
return Render(new Dictionary<string, object?>
|
|
158
|
+
{
|
|
159
|
+
["method"] = sp.Method, ["url"] = sp.Url, ["path"] = sp.Path,
|
|
160
|
+
["headers"] = sp.Headers, ["query"] = sp.Query,
|
|
161
|
+
["params"] = sp.Params, ["body"] = sp.Body,
|
|
162
|
+
});
|
|
163
|
+
case Result r:
|
|
164
|
+
return Render(new Dictionary<string, object?>
|
|
165
|
+
{
|
|
166
|
+
["ok"] = r.Ok, ["status"] = r.Status, ["headers"] = r.Headers,
|
|
167
|
+
["body"] = r.Body, ["resdata"] = r.Resdata,
|
|
168
|
+
["err"] = null == r.Err ? null : r.Err.ToString(),
|
|
169
|
+
});
|
|
170
|
+
case IEntity ent:
|
|
171
|
+
return Render(ent.Data());
|
|
172
|
+
case Context ctx:
|
|
173
|
+
return Render(ctx.ToRecord());
|
|
174
|
+
case IDictionary dict:
|
|
175
|
+
{
|
|
176
|
+
var sb = new StringBuilder("{");
|
|
177
|
+
foreach (DictionaryEntry kv in dict)
|
|
178
|
+
{
|
|
179
|
+
sb.Append(JsonSerializer.Serialize(Convert.ToString(kv.Key)))
|
|
180
|
+
.Append(':').Append(Render(kv.Value)).Append(',');
|
|
181
|
+
}
|
|
182
|
+
return sb.Append('}').ToString();
|
|
183
|
+
}
|
|
184
|
+
case IList list:
|
|
185
|
+
return "[" + string.Join(",", list.Cast<object?>().Select(Render)) + "]";
|
|
186
|
+
case Exception e:
|
|
187
|
+
return e.ToString();
|
|
188
|
+
default:
|
|
189
|
+
try
|
|
190
|
+
{
|
|
191
|
+
return JsonSerializer.Serialize(val, FieldsJson);
|
|
192
|
+
}
|
|
193
|
+
catch (Exception)
|
|
194
|
+
{
|
|
195
|
+
return StructUtils.Jsonify(val, 0);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Every default print a value has, plus the SDK's own record of it.
|
|
201
|
+
private static List<Sink> FormsOf(string name, object? val)
|
|
202
|
+
{
|
|
203
|
+
var out_ = new List<Sink>();
|
|
204
|
+
void Push(string kind, Func<string> fn)
|
|
205
|
+
{
|
|
206
|
+
try { out_.Add(new Sink(name + ":" + kind, fn())); } catch (Exception) { }
|
|
207
|
+
}
|
|
208
|
+
Push("json", () => StructUtils.Jsonify(val, 0));
|
|
209
|
+
Push("string", () => Convert.ToString(val) ?? "");
|
|
210
|
+
Push("render", () => Render(val));
|
|
211
|
+
if (val is ${Name}Error se)
|
|
212
|
+
{
|
|
213
|
+
Push("message", () => se.Message);
|
|
214
|
+
Push("record", () => StructUtils.Jsonify(se.ToRecord(), 0));
|
|
215
|
+
}
|
|
216
|
+
if (val is Exception e)
|
|
217
|
+
{
|
|
218
|
+
Push("message", () => e.Message);
|
|
219
|
+
Push("tostring", () => e.ToString());
|
|
220
|
+
Push("stack", () => e.StackTrace ?? "");
|
|
221
|
+
}
|
|
222
|
+
if (val is Context ctx)
|
|
223
|
+
{
|
|
224
|
+
Push("record", () => StructUtils.Jsonify(ctx.ToRecord(), 0));
|
|
225
|
+
}
|
|
226
|
+
if (val is ${Name}SDK)
|
|
227
|
+
{
|
|
228
|
+
// What a structured logger walking own fields would see.
|
|
229
|
+
Push("fields", () => JsonSerializer.Serialize(val, FieldsJson));
|
|
230
|
+
}
|
|
231
|
+
return out_;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Captures the serialised context from inside the pipeline: what a hook
|
|
235
|
+
// author would hand to a logger.
|
|
236
|
+
private sealed class CaptureFeature : BaseFeature
|
|
237
|
+
{
|
|
238
|
+
private readonly List<Sink> _sinks;
|
|
239
|
+
|
|
240
|
+
public CaptureFeature(List<Sink> sinks)
|
|
241
|
+
{
|
|
242
|
+
Name = "capture";
|
|
243
|
+
Version = "0.0.1";
|
|
244
|
+
Active = true;
|
|
245
|
+
_sinks = sinks;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
public override void PreRequest(Context ctx) => _sinks.AddRange(FormsOf("ctx@PreRequest", ctx));
|
|
249
|
+
public override void PreResponse(Context ctx) => _sinks.AddRange(FormsOf("ctx@PreResponse", ctx));
|
|
250
|
+
public override void PreUnexpected(Context ctx) => _sinks.AddRange(FormsOf("ctx@PreUnexpected", ctx));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// A feature that throws from inside the pipeline, quoting the request it
|
|
254
|
+
// saw: an exception MakeError never handled.
|
|
255
|
+
private sealed class ThrowFeature : BaseFeature
|
|
256
|
+
{
|
|
257
|
+
private readonly bool _unexpected;
|
|
258
|
+
|
|
259
|
+
public ThrowFeature(bool unexpected)
|
|
260
|
+
{
|
|
261
|
+
Name = "throwhook";
|
|
262
|
+
Version = "0.0.1";
|
|
263
|
+
Active = true;
|
|
264
|
+
_unexpected = unexpected;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
public override void PreResponse(Context ctx) =>
|
|
268
|
+
throw new Exception("hook saw " + Render(ctx.Spec));
|
|
269
|
+
|
|
270
|
+
// MakeError fires PreUnexpected, so what this throws escapes it.
|
|
271
|
+
public override void PreUnexpected(Context ctx)
|
|
272
|
+
{
|
|
273
|
+
if (_unexpected)
|
|
274
|
+
{
|
|
275
|
+
throw new Exception("hook saw " + Render(ctx.Spec));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// A stream that succeeds, yielding the result's items.
|
|
281
|
+
private sealed class StreamOkFeature : BaseFeature
|
|
282
|
+
{
|
|
283
|
+
public StreamOkFeature()
|
|
284
|
+
{
|
|
285
|
+
Name = "streamok";
|
|
286
|
+
Version = "0.0.1";
|
|
287
|
+
Active = true;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
public override void PreDone(Context ctx)
|
|
291
|
+
{
|
|
292
|
+
if (null == ctx.Result)
|
|
293
|
+
{
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
var items = ctx.Result.Resdata switch
|
|
297
|
+
{
|
|
298
|
+
IList list => list.Cast<object?>().ToList(),
|
|
299
|
+
null => new List<object?>(),
|
|
300
|
+
var one => new List<object?> { one },
|
|
301
|
+
};
|
|
302
|
+
ctx.Result.Stream = () => items;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// A stream that fails while the caller iterates it, quoting a credential.
|
|
307
|
+
private sealed class StreamThrowFeature : BaseFeature
|
|
308
|
+
{
|
|
309
|
+
public StreamThrowFeature()
|
|
310
|
+
{
|
|
311
|
+
Name = "streamthrow";
|
|
312
|
+
Version = "0.0.1";
|
|
313
|
+
Active = true;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
public override void PreDone(Context ctx)
|
|
317
|
+
{
|
|
318
|
+
if (null != ctx.Result)
|
|
319
|
+
{
|
|
320
|
+
ctx.Result.Stream = Failing;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private static IEnumerable<object?> Failing()
|
|
325
|
+
{
|
|
326
|
+
yield return Fail();
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private static object? Fail() => throw new Exception("stream saw " + CanaryApikey);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
private sealed record Scenario(string Name, Func<string, Dictionary<string, object?>, object?> Respond);
|
|
333
|
+
|
|
334
|
+
private static Dictionary<string, object?> Response(int status, object? data,
|
|
335
|
+
Dictionary<string, object?>? headers = null)
|
|
336
|
+
{
|
|
337
|
+
var h = new Dictionary<string, object?> { ["content-type"] = "application/json" };
|
|
338
|
+
foreach (var kv in headers ?? new Dictionary<string, object?>())
|
|
339
|
+
{
|
|
340
|
+
h[kv.Key.ToLowerInvariant()] = kv.Value;
|
|
341
|
+
}
|
|
342
|
+
return new Dictionary<string, object?>
|
|
343
|
+
{
|
|
344
|
+
["status"] = status,
|
|
345
|
+
["statusText"] = status < 400 ? "OK" : "ERR",
|
|
346
|
+
["json"] = (Func<object?>)(() => data),
|
|
347
|
+
["body"] = StructUtils.Jsonify(data, 0),
|
|
348
|
+
["headers"] = h,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private static readonly List<Scenario> Scenarios = new()
|
|
353
|
+
{
|
|
354
|
+
new Scenario("ok", (_url, _def) => Response(200,
|
|
355
|
+
new Dictionary<string, object?> { ["id"] = "i1", ["name"] = "n1" },
|
|
356
|
+
new Dictionary<string, object?> { ["x-session-token"] = "RESP-TOKEN-a1b2c3d4e5" })),
|
|
357
|
+
new Scenario("notfound", (_url, _def) => Response(404,
|
|
358
|
+
new Dictionary<string, object?> { ["error"] = "no such record" })),
|
|
359
|
+
new Scenario("server", (_url, _def) => Response(500,
|
|
360
|
+
new Dictionary<string, object?> { ["error"] = "boom" })),
|
|
361
|
+
new Scenario("transport", (url, _def) =>
|
|
362
|
+
throw new Exception("socket hang up (URL was: \\"" + url + "\\")")),
|
|
363
|
+
new Scenario("notjson", (_url, _def) => new Dictionary<string, object?>
|
|
364
|
+
{
|
|
365
|
+
["status"] = 200,
|
|
366
|
+
["statusText"] = "OK",
|
|
367
|
+
["json"] = (Func<object?>)(() => throw new Exception("Unexpected token < in JSON")),
|
|
368
|
+
["body"] = "<html>",
|
|
369
|
+
["headers"] = new Dictionary<string, object?>(),
|
|
370
|
+
}),
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
private static ${Name}SDK MakeSdk(Scenario scenario, List<Sink> sinks,
|
|
374
|
+
Dictionary<string, object?>? cleanopts = null, BaseFeature? extra = null)
|
|
375
|
+
{
|
|
376
|
+
Action<Dictionary<string, object?>> Capture(string name) =>
|
|
377
|
+
rec => sinks.AddRange(FormsOf(name, rec));
|
|
378
|
+
|
|
379
|
+
var feature = new Dictionary<string, object?>();
|
|
380
|
+
if (Fh.HasFeature("log"))
|
|
381
|
+
{
|
|
382
|
+
feature["log"] = new Dictionary<string, object?>
|
|
383
|
+
{
|
|
384
|
+
["active"] = true,
|
|
385
|
+
["logger"] = (Action<string, string, Dictionary<string, object?>>)(
|
|
386
|
+
(level, _msg, attrs) => sinks.AddRange(FormsOf("log." + level, attrs))),
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
if (Fh.HasFeature("debug"))
|
|
390
|
+
{
|
|
391
|
+
feature["debug"] = new Dictionary<string, object?>
|
|
392
|
+
{
|
|
393
|
+
["active"] = true, ["onEntry"] = Capture("debug"),
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
if (Fh.HasFeature("audit"))
|
|
397
|
+
{
|
|
398
|
+
feature["audit"] = new Dictionary<string, object?>
|
|
399
|
+
{
|
|
400
|
+
["active"] = true, ["sink"] = Capture("audit"),
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
if (Fh.HasFeature("telemetry"))
|
|
404
|
+
{
|
|
405
|
+
feature["telemetry"] = new Dictionary<string, object?>
|
|
406
|
+
{
|
|
407
|
+
["active"] = true, ["exporter"] = Capture("telemetry"),
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
if (Fh.HasFeature("cost"))
|
|
411
|
+
{
|
|
412
|
+
feature["cost"] = new Dictionary<string, object?>
|
|
413
|
+
{
|
|
414
|
+
["active"] = true,
|
|
415
|
+
["sink"] = (Action<CostRecord>)(rec => sinks.AddRange(FormsOf("cost", rec))),
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
if (Fh.HasFeature("metrics"))
|
|
419
|
+
{
|
|
420
|
+
feature["metrics"] = new Dictionary<string, object?> { ["active"] = true };
|
|
421
|
+
}
|
|
422
|
+
if (Fh.HasFeature("clienttrack"))
|
|
423
|
+
{
|
|
424
|
+
feature["clienttrack"] = new Dictionary<string, object?> { ["active"] = true };
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
var clean = new Dictionary<string, object?> { ["values"] = CanaryValue };
|
|
428
|
+
foreach (var kv in cleanopts ?? new Dictionary<string, object?>())
|
|
429
|
+
{
|
|
430
|
+
clean[kv.Key] = kv.Value;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
var fetcher = (Context _ctx, string url, Dictionary<string, object?> fetchdef) =>
|
|
434
|
+
scenario.Respond(url, fetchdef);
|
|
435
|
+
|
|
436
|
+
var extend = new List<object?> { new CaptureFeature(sinks) };
|
|
437
|
+
if (null != extra)
|
|
438
|
+
{
|
|
439
|
+
extend.Add(extra);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return new ${Name}SDK(new Dictionary<string, object?>
|
|
443
|
+
{
|
|
444
|
+
["apikey"] = CanaryApikey,
|
|
445
|
+
["secret"] = CanarySecret,
|
|
446
|
+
["headers"] = new Dictionary<string, object?> { ["X-Custom-Token"] = CanaryHeader },
|
|
447
|
+
["clean"] = clean,
|
|
448
|
+
["feature"] = feature,
|
|
449
|
+
["extend"] = extend,
|
|
450
|
+
["utility"] = new Dictionary<string, object?> { ["fetcher"] = fetcher },
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Emitted from the model: every active entity with the operations it
|
|
455
|
+
// declares, list and load first.
|
|
456
|
+
private static readonly List<Candidate> Candidates = new()
|
|
457
|
+
{
|
|
458
|
+
${candidateLines}
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
private static object? Invoke(${Name}EntityBase ent, string op,
|
|
462
|
+
Dictionary<string, object?> match, Dictionary<string, object?>? ctrl)
|
|
463
|
+
{
|
|
464
|
+
var args = new Dictionary<string, object?>(match);
|
|
465
|
+
return op switch
|
|
466
|
+
{
|
|
467
|
+
"list" => ent.List(args, ctrl),
|
|
468
|
+
"load" => ent.Load(args, ctrl),
|
|
469
|
+
"create" => ent.Create(args, ctrl),
|
|
470
|
+
"update" => ent.Update(args, ctrl),
|
|
471
|
+
"remove" => ent.Remove(args, ctrl),
|
|
472
|
+
_ => throw new InvalidOperationException("unknown operation: " + op),
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Every path parameter an op's points declare, filled in.
|
|
477
|
+
private static Dictionary<string, object?> Filled(${Name}SDK sdk, string entity, string op)
|
|
478
|
+
{
|
|
479
|
+
var filled = new Dictionary<string, object?>();
|
|
480
|
+
var points = StructUtils.GetPath(sdk.GetRootCtx().Config,
|
|
481
|
+
StructUtils.Jt("entity", entity, "op", op, "points")) as List<object?>;
|
|
482
|
+
foreach (var point in points ?? new List<object?>())
|
|
483
|
+
{
|
|
484
|
+
var ps = StructUtils.GetPath(point, StructUtils.Jt("args", "params")) as List<object?>;
|
|
485
|
+
foreach (var p in ps ?? new List<object?>())
|
|
486
|
+
{
|
|
487
|
+
if (StructUtils.GetProp(p, "name") is string pname)
|
|
488
|
+
{
|
|
489
|
+
filled[pname] = "p1";
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return filled;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// The first operation that completes against a plain 200: with no
|
|
497
|
+
// arguments, else with every path parameter its points declare filled in.
|
|
498
|
+
private static Target? UsableOp()
|
|
499
|
+
{
|
|
500
|
+
var fetcher = (Context _ctx, string _url, Dictionary<string, object?> _def) =>
|
|
501
|
+
(object?)Response(200, new Dictionary<string, object?> { ["id"] = "i1" });
|
|
502
|
+
var plain = new ${Name}SDK(new Dictionary<string, object?>
|
|
503
|
+
{
|
|
504
|
+
["apikey"] = CanaryApikey,
|
|
505
|
+
["utility"] = new Dictionary<string, object?> { ["fetcher"] = fetcher },
|
|
506
|
+
});
|
|
507
|
+
foreach (var candidate in Candidates)
|
|
508
|
+
{
|
|
509
|
+
foreach (var op in candidate.Ops)
|
|
510
|
+
{
|
|
511
|
+
foreach (var match in new[] { new Dictionary<string, object?>(), Filled(plain, candidate.Name, op) })
|
|
512
|
+
{
|
|
513
|
+
try
|
|
514
|
+
{
|
|
515
|
+
Invoke(candidate.Accessor(plain), op, match, null);
|
|
516
|
+
return new Target(candidate, op, match);
|
|
517
|
+
}
|
|
518
|
+
catch (Exception)
|
|
519
|
+
{
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
private const string NoOp = "no operation of this SDK completes against a plain 200; nothing to sweep";
|
|
529
|
+
|
|
530
|
+
private static Exception? Drive(${Name}SDK sdk, Target target,
|
|
531
|
+
Dictionary<string, object?> ctrl, List<Sink> sinks)
|
|
532
|
+
{
|
|
533
|
+
// A caller may keep the record it passed rather than read ctrl's entry.
|
|
534
|
+
var held = ctrl.GetValueOrDefault("explain");
|
|
535
|
+
object? out_ = null;
|
|
536
|
+
Exception? err = null;
|
|
537
|
+
try
|
|
538
|
+
{
|
|
539
|
+
out_ = Invoke(target.Candidate.Accessor(sdk), target.Op, target.Match, ctrl);
|
|
540
|
+
}
|
|
541
|
+
catch (Exception e)
|
|
542
|
+
{
|
|
543
|
+
err = e;
|
|
544
|
+
}
|
|
545
|
+
if (null != err) sinks.AddRange(FormsOf("error", err));
|
|
546
|
+
if (null != out_) sinks.AddRange(FormsOf("result", out_));
|
|
547
|
+
if (ctrl.TryGetValue("explain", out var explain) && null != explain)
|
|
548
|
+
{
|
|
549
|
+
sinks.AddRange(FormsOf("explain", explain));
|
|
550
|
+
}
|
|
551
|
+
if (null != held && !ReferenceEquals(held, ctrl.GetValueOrDefault("explain")))
|
|
552
|
+
{
|
|
553
|
+
sinks.AddRange(FormsOf("explain:held", held));
|
|
554
|
+
}
|
|
555
|
+
return err;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
private void Report(string line)
|
|
559
|
+
{
|
|
560
|
+
// Both: xunit shows the helper's output, the lane reads the console.
|
|
561
|
+
Console.WriteLine(line);
|
|
562
|
+
_out.WriteLine(line);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
[Fact]
|
|
566
|
+
public async Task NoCredentialLeavesTheSdkInAnyForm()
|
|
567
|
+
{
|
|
568
|
+
var target = UsableOp();
|
|
569
|
+
if (null == target)
|
|
570
|
+
{
|
|
571
|
+
// xunit 2 has no runtime skip, so the skip is a printed reason.
|
|
572
|
+
Report("clean: skipped: " + NoOp);
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
var sinks = new List<Sink>();
|
|
577
|
+
var errors = new Dictionary<string, Exception>();
|
|
578
|
+
var explains = new Dictionary<string, Dictionary<string, object?>>();
|
|
579
|
+
|
|
580
|
+
var variants = new (string Name, Func<Dictionary<string, object?>> Ctrl)[]
|
|
581
|
+
{
|
|
582
|
+
("throw", () => new Dictionary<string, object?>()),
|
|
583
|
+
("explain", () => new Dictionary<string, object?>
|
|
584
|
+
{
|
|
585
|
+
["explain"] = new Dictionary<string, object?>(),
|
|
586
|
+
}),
|
|
587
|
+
("nothrow", () => new Dictionary<string, object?>
|
|
588
|
+
{
|
|
589
|
+
["throw"] = false,
|
|
590
|
+
["explain"] = new Dictionary<string, object?>(),
|
|
591
|
+
}),
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
foreach (var scenario in Scenarios)
|
|
595
|
+
{
|
|
596
|
+
foreach (var variant in variants)
|
|
597
|
+
{
|
|
598
|
+
var sdk = MakeSdk(scenario, sinks);
|
|
599
|
+
var ctrl = variant.Ctrl();
|
|
600
|
+
var err = Drive(sdk, target, ctrl, sinks);
|
|
601
|
+
var key = scenario.Name + "/" + variant.Name;
|
|
602
|
+
if (null != err) errors[key] = err;
|
|
603
|
+
if (ctrl.TryGetValue("explain", out var ex) && ex is Dictionary<string, object?> exm)
|
|
604
|
+
{
|
|
605
|
+
explains[key] = exm;
|
|
606
|
+
}
|
|
607
|
+
sinks.AddRange(FormsOf("sdk", sdk));
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// A credential mistyped as a map is rejected by validation, whose
|
|
612
|
+
// message quotes the value it rejected.
|
|
613
|
+
Exception? rejected = null;
|
|
614
|
+
try
|
|
615
|
+
{
|
|
616
|
+
new ${Name}SDK(new Dictionary<string, object?>
|
|
617
|
+
{
|
|
618
|
+
["apikey"] = new Dictionary<string, object?> { ["value"] = CanaryApikey },
|
|
619
|
+
["clean"] = new Dictionary<string, object?> { ["values"] = CanaryValue },
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
catch (Exception e)
|
|
623
|
+
{
|
|
624
|
+
rejected = e;
|
|
625
|
+
}
|
|
626
|
+
Assert.True(null != rejected, "a credential mistyped as a map should be rejected");
|
|
627
|
+
sinks.AddRange(FormsOf("rejected", rejected));
|
|
628
|
+
|
|
629
|
+
// An exception a feature hook throws, quoting the request, skips MakeError.
|
|
630
|
+
foreach (var unexpected in new[] { false, true })
|
|
631
|
+
{
|
|
632
|
+
var hooked = MakeSdk(Scenarios[0], sinks, null, new ThrowFeature(unexpected));
|
|
633
|
+
var hookerr = Drive(hooked, target, new Dictionary<string, object?>
|
|
634
|
+
{
|
|
635
|
+
["explain"] = new Dictionary<string, object?>(),
|
|
636
|
+
}, sinks);
|
|
637
|
+
Assert.True(null != hookerr, "the throwing hook should fail the operation");
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// Iterating a stream runs inside the same catch path as the operation,
|
|
641
|
+
// and the explain record the caller passed is cleaned however it ends.
|
|
642
|
+
foreach (var (name, extra) in new (string, BaseFeature?)[]
|
|
643
|
+
{
|
|
644
|
+
("stream", new StreamThrowFeature()),
|
|
645
|
+
("stream-ok", new StreamOkFeature()),
|
|
646
|
+
("stream-plain", null),
|
|
647
|
+
})
|
|
648
|
+
{
|
|
649
|
+
var streaming = target.Candidate.Accessor(MakeSdk(Scenarios[0], sinks, null, extra));
|
|
650
|
+
var explain = new Dictionary<string, object?>();
|
|
651
|
+
Exception? streamerr = null;
|
|
652
|
+
try
|
|
653
|
+
{
|
|
654
|
+
await foreach (var _ in streaming.Stream(target.Op, new Dictionary<string, object?>
|
|
655
|
+
{
|
|
656
|
+
["reqmatch"] = new Dictionary<string, object?>(target.Match),
|
|
657
|
+
}, new Dictionary<string, object?>
|
|
658
|
+
{
|
|
659
|
+
["ctrl"] = new Dictionary<string, object?> { ["explain"] = explain },
|
|
660
|
+
}))
|
|
661
|
+
{
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
catch (Exception e)
|
|
665
|
+
{
|
|
666
|
+
streamerr = e;
|
|
667
|
+
}
|
|
668
|
+
Assert.True(("stream" == name) == (null != streamerr), name + ": only the failing stream throws");
|
|
669
|
+
if (null != streamerr)
|
|
670
|
+
{
|
|
671
|
+
sinks.AddRange(FormsOf(name, streamerr));
|
|
672
|
+
}
|
|
673
|
+
Assert.True(0 < explain.Count, name + ": the explain record was not filled");
|
|
674
|
+
sinks.AddRange(FormsOf(name + ":explain", explain));
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// The raw path returns its failure rather than throwing it.
|
|
678
|
+
var raw = MakeSdk(Scenarios[3], sinks).Direct(new Dictionary<string, object?> { ["path"] = "raw" });
|
|
679
|
+
Assert.True(Equals(false, raw.GetValueOrDefault("ok")) && null != raw.GetValueOrDefault("err"),
|
|
680
|
+
"a transport failure should fail Direct()");
|
|
681
|
+
sinks.AddRange(FormsOf("direct", raw["err"]));
|
|
682
|
+
|
|
683
|
+
// A registered value used as a map key is masked; keys that mask alike
|
|
684
|
+
// are kept apart.
|
|
685
|
+
var probe = MakeSdk(Scenarios[0], sinks);
|
|
686
|
+
var named = probe.GetUtility().Clean(probe.GetRootCtx(), new Dictionary<string, object?>
|
|
687
|
+
{
|
|
688
|
+
[CanaryValue] = 1, [CanaryHeader] = 2, ["plain"] = 3,
|
|
689
|
+
}) as Dictionary<string, object?> ?? new Dictionary<string, object?>();
|
|
690
|
+
sinks.AddRange(FormsOf("named", named));
|
|
691
|
+
|
|
692
|
+
// An error's code is cleaned like its message.
|
|
693
|
+
var coded = probe.GetUtility().Clean(probe.GetRootCtx(),
|
|
694
|
+
new ${Name}Error("code_" + CanaryValue, "coded", null));
|
|
695
|
+
sinks.AddRange(FormsOf("coded", coded));
|
|
696
|
+
|
|
697
|
+
var leaked = sinks
|
|
698
|
+
.Select(s => (s.Name, Found: Leaks(s.Text)))
|
|
699
|
+
.Where(s => 0 < s.Found.Count)
|
|
700
|
+
.ToList();
|
|
701
|
+
|
|
702
|
+
Report("clean: swept " + sinks.Count + " surface(s), " + leaked.Count + " leak(s)");
|
|
703
|
+
|
|
704
|
+
Assert.True(0 == leaked.Count, "credential leaked through: " +
|
|
705
|
+
string.Join("; ", leaked.Select(l => l.Name + " [" + string.Join(", ", l.Found) + "]")));
|
|
706
|
+
|
|
707
|
+
// The positive half: the slot the credential travelled in is masked,
|
|
708
|
+
// and an unregistered token in a response header is masked by name.
|
|
709
|
+
Assert.True(errors.TryGetValue("notfound/throw", out var notfoundErr), "the 404 scenario must throw");
|
|
710
|
+
var notfound = Assert.IsType<${Name}Error>(notfoundErr);
|
|
711
|
+
Assert.Equal(404, notfound.Status);
|
|
712
|
+
var spec = notfound.SpecVal as Dictionary<string, object?> ?? new Dictionary<string, object?>();
|
|
713
|
+
if (!AuthSuppressed)
|
|
714
|
+
{
|
|
715
|
+
if ("query" == AuthWhere)
|
|
716
|
+
{
|
|
717
|
+
Assert.Equal(Mask, Header(spec.GetValueOrDefault("query"), AuthName));
|
|
718
|
+
}
|
|
719
|
+
else if ("cookie" == AuthWhere)
|
|
720
|
+
{
|
|
721
|
+
var cookie = Convert.ToString(Header(spec.GetValueOrDefault("headers"), "cookie")) ?? "";
|
|
722
|
+
Assert.True(cookie.Contains(Mask), "cookie: " + cookie);
|
|
723
|
+
}
|
|
724
|
+
else
|
|
725
|
+
{
|
|
726
|
+
var cred = Convert.ToString(Header(spec.GetValueOrDefault("headers"), AuthName)) ?? "";
|
|
727
|
+
Assert.True(cred.EndsWith(Mask), AuthName + ": " + cred);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
Assert.Equal(Mask, Header(spec.GetValueOrDefault("headers"), "x-custom-token"));
|
|
731
|
+
|
|
732
|
+
Assert.True(explains.TryGetValue("ok/explain", out var explained), "the ok scenario should explain");
|
|
733
|
+
var result = explained!.GetValueOrDefault("result") as Dictionary<string, object?>;
|
|
734
|
+
Assert.True(null != result, "the explain record should carry the result");
|
|
735
|
+
Assert.Equal(Mask, Header(result!.GetValueOrDefault("headers"), "x-session-token"));
|
|
736
|
+
|
|
737
|
+
Assert.True(3 == named.Count && Equals(1, named.GetValueOrDefault(Mask)) &&
|
|
738
|
+
Equals(2, named.GetValueOrDefault(Mask + "#1")) && Equals(3, named.GetValueOrDefault("plain")),
|
|
739
|
+
"map keys: " + Render(named));
|
|
740
|
+
Assert.Equal("code_" + Mask, (coded as ${Name}Error)?.Code);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
[Fact]
|
|
744
|
+
public void TheSweepCanSeeALeakCleanSwitchedOffShowsTheCredential()
|
|
745
|
+
{
|
|
746
|
+
var target = UsableOp();
|
|
747
|
+
if (null == target)
|
|
748
|
+
{
|
|
749
|
+
Report("clean: skipped: " + NoOp);
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
var sinks = new List<Sink>();
|
|
754
|
+
var sdk = MakeSdk(Scenarios[1], sinks, new Dictionary<string, object?> { ["active"] = false });
|
|
755
|
+
var err = Drive(sdk, target, new Dictionary<string, object?>(), sinks);
|
|
756
|
+
Assert.True(null != err, "the 404 scenario must throw");
|
|
757
|
+
|
|
758
|
+
// Explaining a failure must not cost it its error.
|
|
759
|
+
var explained = Drive(MakeSdk(Scenarios[1], new List<Sink>(),
|
|
760
|
+
new Dictionary<string, object?> { ["active"] = false }), target,
|
|
761
|
+
new Dictionary<string, object?> { ["explain"] = new Dictionary<string, object?>() }, new List<Sink>());
|
|
762
|
+
Assert.True(err?.Message == explained?.Message,
|
|
763
|
+
"with clean off, explain lost the error: expected " + err?.Message + ", got " + explained?.Message);
|
|
764
|
+
|
|
765
|
+
var leaked = sinks.Where(s => 0 < Leaks(s.Text).Count).ToList();
|
|
766
|
+
Assert.True(0 < leaked.Count, "with clean off, nothing showed the canary: the sweep is blind");
|
|
767
|
+
|
|
768
|
+
if (!AuthSuppressed)
|
|
769
|
+
{
|
|
770
|
+
var text = Render((err as ${Name}Error)?.SpecVal);
|
|
771
|
+
Assert.True(text.Contains(CanaryApikey) ||
|
|
772
|
+
text.Contains(Convert.ToBase64String(Encoding.UTF8.GetBytes(CanaryApikey + ":" + CanarySecret))),
|
|
773
|
+
"the raw spec should carry the credential when clean is off");
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// A feature's name is not a field name: only the sensitive names inside
|
|
778
|
+
// its settings register. An entity block, of per-entity settings or
|
|
779
|
+
// seeded records keyed by entity name and id, is not read at all.
|
|
780
|
+
[Fact]
|
|
781
|
+
public void AFeatureNameDoesNotMakeItsSettingsSecret()
|
|
782
|
+
{
|
|
783
|
+
var sdk = new ${Name}SDK(new Dictionary<string, object?>
|
|
784
|
+
{
|
|
785
|
+
["apikey"] = CanaryApikey,
|
|
786
|
+
["feature"] = new Dictionary<string, object?>
|
|
787
|
+
{
|
|
788
|
+
["zzsecrets"] = new Dictionary<string, object?>
|
|
789
|
+
{
|
|
790
|
+
["active"] = false, ["kind"] = "PLAINSETTING-q8w2e4r6",
|
|
791
|
+
},
|
|
792
|
+
["zzfeat"] = new Dictionary<string, object?>
|
|
793
|
+
{
|
|
794
|
+
["active"] = false, ["apitoken"] = "FEATTOKEN-z9y8x7w6",
|
|
795
|
+
},
|
|
796
|
+
["test"] = new Dictionary<string, object?>
|
|
797
|
+
{
|
|
798
|
+
["active"] = false,
|
|
799
|
+
["entity"] = new Dictionary<string, object?>
|
|
800
|
+
{
|
|
801
|
+
["zztoken"] = new Dictionary<string, object?>
|
|
802
|
+
{
|
|
803
|
+
["ZZTOKEN01"] = new Dictionary<string, object?> { ["note"] = "PLAINRECORD-t5r3e1w9" },
|
|
804
|
+
},
|
|
805
|
+
},
|
|
806
|
+
},
|
|
807
|
+
},
|
|
808
|
+
["entity"] = new Dictionary<string, object?>
|
|
809
|
+
{
|
|
810
|
+
["zztoken"] = new Dictionary<string, object?>
|
|
811
|
+
{
|
|
812
|
+
["alias"] = new Dictionary<string, object?> { ["zzkey"] = "PLAINALIAS-m2n4b6v8" },
|
|
813
|
+
},
|
|
814
|
+
},
|
|
815
|
+
});
|
|
816
|
+
object? Cleaned(string s) => sdk.GetUtility().Clean(sdk.GetRootCtx(), s);
|
|
817
|
+
|
|
818
|
+
Assert.Equal("kind PLAINSETTING-q8w2e4r6", Cleaned("kind PLAINSETTING-q8w2e4r6"));
|
|
819
|
+
Assert.Equal("token " + Mask, Cleaned("token FEATTOKEN-z9y8x7w6"));
|
|
820
|
+
Assert.Equal("record PLAINRECORD-t5r3e1w9", Cleaned("record PLAINRECORD-t5r3e1w9"));
|
|
821
|
+
Assert.Equal("alias PLAINALIAS-m2n4b6v8", Cleaned("alias PLAINALIAS-m2n4b6v8"));
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
[Fact]
|
|
825
|
+
public void TheGeneratedConfigsOwnCleanBlockIsHonoured()
|
|
826
|
+
{
|
|
827
|
+
var utility = new ${Name}SDK(new Dictionary<string, object?>()).GetUtility();
|
|
828
|
+
var config = new Dictionary<string, object?>
|
|
829
|
+
{
|
|
830
|
+
["options"] = new Dictionary<string, object?>
|
|
831
|
+
{
|
|
832
|
+
["clean"] = new Dictionary<string, object?>
|
|
833
|
+
{
|
|
834
|
+
["keys"] = "zzsens",
|
|
835
|
+
["values"] = "CONFIG-SEEDED-1",
|
|
836
|
+
},
|
|
837
|
+
},
|
|
838
|
+
};
|
|
839
|
+
var ctx = utility.MakeContext(new Dictionary<string, object?>
|
|
840
|
+
{
|
|
841
|
+
["utility"] = utility,
|
|
842
|
+
["config"] = config,
|
|
843
|
+
["options"] = new Dictionary<string, object?>
|
|
844
|
+
{
|
|
845
|
+
["clean"] = new Dictionary<string, object?> { ["values"] = "CALLER-SEEDED-2" },
|
|
846
|
+
},
|
|
847
|
+
}, null);
|
|
848
|
+
ctx.Options = utility.MakeOptions(ctx);
|
|
849
|
+
|
|
850
|
+
Assert.Equal("a " + Mask + " b " + Mask, utility.Clean(ctx, "a CONFIG-SEEDED-1 b CALLER-SEEDED-2"));
|
|
851
|
+
var masked = utility.Clean(ctx, new Dictionary<string, object?>
|
|
852
|
+
{
|
|
853
|
+
["my_zzsens"] = "x",
|
|
854
|
+
["other"] = "y",
|
|
855
|
+
}) as Dictionary<string, object?>;
|
|
856
|
+
Assert.Equal(Mask, masked?.GetValueOrDefault("my_zzsens"));
|
|
857
|
+
Assert.Equal("y", masked?.GetValueOrDefault("other"));
|
|
858
|
+
Assert.Equal("CONFIG-SEEDED-1",
|
|
859
|
+
StructUtils.GetPath(config, StructUtils.Jt("options", "clean", "values")));
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
[Fact]
|
|
863
|
+
public void WithNoCleanBlockTheSchemaDefaultsApply()
|
|
864
|
+
{
|
|
865
|
+
var utility = new ${Name}SDK(new Dictionary<string, object?>()).GetUtility();
|
|
866
|
+
var ctx = utility.MakeContext(new Dictionary<string, object?>
|
|
867
|
+
{
|
|
868
|
+
["utility"] = utility,
|
|
869
|
+
["options"] = new Dictionary<string, object?> { ["apikey"] = "NOBLOCK-APIKEY-k3j5h7" },
|
|
870
|
+
}, null);
|
|
871
|
+
ctx.Options = utility.MakeOptions(ctx);
|
|
872
|
+
|
|
873
|
+
Assert.Equal("failed with " + Mask, utility.Clean(ctx, "failed with NOBLOCK-APIKEY-k3j5h7"));
|
|
874
|
+
var masked = utility.Clean(ctx, new Dictionary<string, object?>
|
|
875
|
+
{
|
|
876
|
+
["x-session-token"] = "RESP-TOKEN-a1b2c3d4e5",
|
|
877
|
+
}) as Dictionary<string, object?>;
|
|
878
|
+
Assert.Equal(Mask, masked?.GetValueOrDefault("x-session-token"));
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
`
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
|
|
885
|
+
export {
|
|
886
|
+
TestClean
|
|
887
|
+
}
|