@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,804 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cmp,
|
|
3
|
+
File,
|
|
4
|
+
Content,
|
|
5
|
+
isAuthSuppressed,
|
|
6
|
+
isHttpBasicAuth,
|
|
7
|
+
resolveAuthIn,
|
|
8
|
+
resolveAuthName,
|
|
9
|
+
} from '@voxgig/sdkgen'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
// The canary sweep: every credential slot holds a distinctive value, every
|
|
13
|
+
// diagnostic feature this SDK ships is switched on with a capturing sink, a
|
|
14
|
+
// real operation runs through every outcome, and every string that leaves
|
|
15
|
+
// the SDK is searched for the canaries and their encoded forms. It also
|
|
16
|
+
// proves its own sensitivity: with clean switched off the canary MUST show.
|
|
17
|
+
const TestClean = cmp(function TestClean(props: any) {
|
|
18
|
+
const { model } = props.ctx$
|
|
19
|
+
const { target, gomodule } = props
|
|
20
|
+
|
|
21
|
+
const auth = {
|
|
22
|
+
suppressed: isAuthSuppressed(model),
|
|
23
|
+
where: resolveAuthIn(model),
|
|
24
|
+
name: 'header' === resolveAuthIn(model)
|
|
25
|
+
? resolveAuthName(model).toLowerCase() : resolveAuthName(model),
|
|
26
|
+
basic: isHttpBasicAuth(model),
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
File({ name: 'clean_test.' + target.ext }, () => Content(render(gomodule, model.const.Name, auth)))
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
function render(gomodule: string, Name: string, auth: {
|
|
34
|
+
suppressed: boolean, where: string, name: string, basic: boolean
|
|
35
|
+
}): string {
|
|
36
|
+
return `package sdktest
|
|
37
|
+
|
|
38
|
+
import (
|
|
39
|
+
"context"
|
|
40
|
+
"encoding/base64"
|
|
41
|
+
"encoding/json"
|
|
42
|
+
"fmt"
|
|
43
|
+
"log/slog"
|
|
44
|
+
"net/url"
|
|
45
|
+
"reflect"
|
|
46
|
+
"sort"
|
|
47
|
+
"strings"
|
|
48
|
+
"testing"
|
|
49
|
+
|
|
50
|
+
sdk "${gomodule}"
|
|
51
|
+
"${gomodule}/core"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
// Generated: the credential's wire placement is fixed when the SDK is built.
|
|
55
|
+
var cleanAuth = struct {
|
|
56
|
+
suppressed bool
|
|
57
|
+
where string
|
|
58
|
+
name string
|
|
59
|
+
basic bool
|
|
60
|
+
}{${auth.suppressed}, ${JSON.stringify(auth.where)}, ${JSON.stringify(auth.name)}, ${auth.basic}}
|
|
61
|
+
|
|
62
|
+
var cleanCanary = map[string]string{
|
|
63
|
+
"apikey": "CANARY-APIKEY-k9x2m7q4p1",
|
|
64
|
+
"secret": "CANARY-SECRET-w3e8r5t2y6",
|
|
65
|
+
"header": "CANARY-HEADER-z1x4c7v0b3",
|
|
66
|
+
"value": "CANARY-VALUE-n5m8b2v9c4",
|
|
67
|
+
"config": "CANARY-CONFIG-h6j3k8l2m5",
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const cleanMask = "[redacted]"
|
|
71
|
+
|
|
72
|
+
// Every form a canary can travel in.
|
|
73
|
+
var cleanForms = func() []string {
|
|
74
|
+
out := []string{}
|
|
75
|
+
for _, k := range []string{"apikey", "secret", "header", "value", "config"} {
|
|
76
|
+
v := cleanCanary[k]
|
|
77
|
+
out = append(out, v, base64.StdEncoding.EncodeToString([]byte(v)), url.QueryEscape(v))
|
|
78
|
+
}
|
|
79
|
+
return append(out, base64.StdEncoding.EncodeToString(
|
|
80
|
+
[]byte(cleanCanary["apikey"]+":"+cleanCanary["secret"])))
|
|
81
|
+
}()
|
|
82
|
+
|
|
83
|
+
type cleanSink struct {
|
|
84
|
+
name string
|
|
85
|
+
text string
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Header maps keep the caller's spelling; the assertion should not care.
|
|
89
|
+
func cleanHeader(m any, name string) any {
|
|
90
|
+
hm, _ := m.(map[string]any)
|
|
91
|
+
for k, v := range hm {
|
|
92
|
+
if strings.EqualFold(k, name) {
|
|
93
|
+
return v
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return nil
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
func cleanLeaks(text string) []string {
|
|
100
|
+
found := []string{}
|
|
101
|
+
for _, f := range cleanForms {
|
|
102
|
+
if strings.Contains(text, f) {
|
|
103
|
+
found = append(found, f)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return found
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// The default prints and the JSON form of a value: what a log line, a panic
|
|
110
|
+
// message or a structured logger would carry.
|
|
111
|
+
func cleanSurfaces(name string, val any) []cleanSink {
|
|
112
|
+
out := []cleanSink{}
|
|
113
|
+
if raw, err := json.Marshal(val); err == nil {
|
|
114
|
+
out = append(out, cleanSink{name + ":json", string(raw)})
|
|
115
|
+
}
|
|
116
|
+
out = append(out,
|
|
117
|
+
cleanSink{name + ":v", fmt.Sprintf("%v", val)},
|
|
118
|
+
cleanSink{name + ":+v", fmt.Sprintf("%+v", val)},
|
|
119
|
+
cleanSink{name + ":#v", fmt.Sprintf("%#v", val)},
|
|
120
|
+
)
|
|
121
|
+
if err, ok := val.(error); ok {
|
|
122
|
+
out = append(out, cleanSink{name + ":message", err.Error()})
|
|
123
|
+
}
|
|
124
|
+
return out
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Captures the serialised context from inside the pipeline: what a hook
|
|
128
|
+
// author would hand to a logger.
|
|
129
|
+
type cleanCapture struct {
|
|
130
|
+
sdk.BaseFeature
|
|
131
|
+
sinks *[]cleanSink
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
func newCleanCapture(sinks *[]cleanSink) *cleanCapture {
|
|
135
|
+
return &cleanCapture{
|
|
136
|
+
BaseFeature: sdk.BaseFeature{Version: "0.0.1", Name: "capture", Active: true},
|
|
137
|
+
sinks: sinks,
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
func (f *cleanCapture) take(name string, ctx *sdk.Context) {
|
|
142
|
+
*f.sinks = append(*f.sinks, cleanSurfaces(name, ctx)...)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
func (f *cleanCapture) PreRequest(ctx *sdk.Context) { f.take("ctx@PreRequest", ctx) }
|
|
146
|
+
func (f *cleanCapture) PreResponse(ctx *sdk.Context) { f.take("ctx@PreResponse", ctx) }
|
|
147
|
+
|
|
148
|
+
// The SDK's own error as a hook reads it, which an observability feature logs.
|
|
149
|
+
func (f *cleanCapture) PreUnexpected(ctx *sdk.Context) {
|
|
150
|
+
f.take("ctx@PreUnexpected", ctx)
|
|
151
|
+
if ctx.Ctrl == nil {
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
if sdkerr, ok := ctx.Ctrl.Err.(*sdk.${Name}Error); ok {
|
|
155
|
+
*f.sinks = append(*f.sinks, cleanSurfaces("ctrl.err@PreUnexpected", sdkerr)...)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// A slog handler that keeps every record, attributes included.
|
|
160
|
+
type cleanLogHandler struct {
|
|
161
|
+
name string
|
|
162
|
+
sinks *[]cleanSink
|
|
163
|
+
attrs []slog.Attr
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
func (h *cleanLogHandler) Enabled(context.Context, slog.Level) bool { return true }
|
|
167
|
+
|
|
168
|
+
func (h *cleanLogHandler) Handle(_ context.Context, r slog.Record) error {
|
|
169
|
+
rec := map[string]any{"msg": r.Message}
|
|
170
|
+
for _, a := range h.attrs {
|
|
171
|
+
rec[a.Key] = a.Value.Any()
|
|
172
|
+
}
|
|
173
|
+
r.Attrs(func(a slog.Attr) bool {
|
|
174
|
+
rec[a.Key] = a.Value.Any()
|
|
175
|
+
return true
|
|
176
|
+
})
|
|
177
|
+
*h.sinks = append(*h.sinks,
|
|
178
|
+
cleanSurfaces(h.name+"."+strings.ToLower(r.Level.String()), rec)...)
|
|
179
|
+
return nil
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
func (h *cleanLogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
|
183
|
+
return &cleanLogHandler{
|
|
184
|
+
name: h.name,
|
|
185
|
+
sinks: h.sinks,
|
|
186
|
+
attrs: append(append([]slog.Attr{}, h.attrs...), attrs...),
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
func (h *cleanLogHandler) WithGroup(string) slog.Handler { return h }
|
|
191
|
+
|
|
192
|
+
type cleanScenario struct {
|
|
193
|
+
name string
|
|
194
|
+
respond func(url string, fetchdef map[string]any) (any, error)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
func cleanResponse(status int, data any, headers map[string]any) map[string]any {
|
|
198
|
+
h := map[string]any{"content-type": "application/json"}
|
|
199
|
+
for k, v := range headers {
|
|
200
|
+
h[k] = v
|
|
201
|
+
}
|
|
202
|
+
statusText := "OK"
|
|
203
|
+
if status >= 400 {
|
|
204
|
+
statusText = "ERR"
|
|
205
|
+
}
|
|
206
|
+
raw, _ := json.Marshal(data)
|
|
207
|
+
return map[string]any{
|
|
208
|
+
"status": status,
|
|
209
|
+
"statusText": statusText,
|
|
210
|
+
"headers": h,
|
|
211
|
+
"json": (func() any)(func() any { return data }),
|
|
212
|
+
"body": string(raw),
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
var cleanScenarios = []cleanScenario{
|
|
217
|
+
{"ok", func(string, map[string]any) (any, error) {
|
|
218
|
+
return cleanResponse(200, map[string]any{"id": "i1", "name": "n1"},
|
|
219
|
+
map[string]any{"x-session-token": "RESP-TOKEN-a1b2c3d4e5"}), nil
|
|
220
|
+
}},
|
|
221
|
+
{"notfound", func(string, map[string]any) (any, error) {
|
|
222
|
+
return cleanResponse(404, map[string]any{"error": "no such record"}, nil), nil
|
|
223
|
+
}},
|
|
224
|
+
{"server", func(string, map[string]any) (any, error) {
|
|
225
|
+
return cleanResponse(500, map[string]any{"error": "boom"}, nil), nil
|
|
226
|
+
}},
|
|
227
|
+
{"transport", func(url string, _ map[string]any) (any, error) {
|
|
228
|
+
return nil, fmt.Errorf("socket hang up (URL was: %q)", url)
|
|
229
|
+
}},
|
|
230
|
+
// The SDK's own error, its code quoting a registered value.
|
|
231
|
+
{"coded", func(string, map[string]any) (any, error) {
|
|
232
|
+
return nil, core.New${Name}Error("denied_"+cleanCanary["apikey"], "coded failure", nil)
|
|
233
|
+
}},
|
|
234
|
+
{"notjson", func(string, map[string]any) (any, error) {
|
|
235
|
+
return map[string]any{
|
|
236
|
+
"status": 200,
|
|
237
|
+
"statusText": "OK",
|
|
238
|
+
"headers": map[string]any{},
|
|
239
|
+
"json": (func() any)(func() any { return nil }),
|
|
240
|
+
"body": "<html>",
|
|
241
|
+
}, nil
|
|
242
|
+
}},
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
func cleanMakeSdk(scenario cleanScenario, sinks *[]cleanSink, cleanopts map[string]any,
|
|
246
|
+
extra ...any) *sdk.${Name}SDK {
|
|
247
|
+
capture := func(name string) func(map[string]any) {
|
|
248
|
+
return func(rec map[string]any) {
|
|
249
|
+
*sinks = append(*sinks, cleanSurfaces(name, rec)...)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
feature := map[string]any{}
|
|
254
|
+
if fhHasFeature("log") {
|
|
255
|
+
feature["log"] = map[string]any{
|
|
256
|
+
"active": true,
|
|
257
|
+
"logger": slog.New(&cleanLogHandler{name: "log", sinks: sinks}),
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if fhHasFeature("debug") {
|
|
261
|
+
feature["debug"] = map[string]any{"active": true, "onEntry": capture("debug")}
|
|
262
|
+
}
|
|
263
|
+
if fhHasFeature("audit") {
|
|
264
|
+
feature["audit"] = map[string]any{"active": true, "sink": capture("audit")}
|
|
265
|
+
}
|
|
266
|
+
if fhHasFeature("telemetry") {
|
|
267
|
+
feature["telemetry"] = map[string]any{"active": true, "exporter": capture("telemetry")}
|
|
268
|
+
}
|
|
269
|
+
if fhHasFeature("cost") {
|
|
270
|
+
feature["cost"] = map[string]any{"active": true, "sink": capture("cost")}
|
|
271
|
+
}
|
|
272
|
+
if fhHasFeature("metrics") {
|
|
273
|
+
feature["metrics"] = map[string]any{"active": true}
|
|
274
|
+
}
|
|
275
|
+
if fhHasFeature("clienttrack") {
|
|
276
|
+
feature["clienttrack"] = map[string]any{"active": true}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
clean := map[string]any{"values": cleanCanary["value"]}
|
|
280
|
+
for k, v := range cleanopts {
|
|
281
|
+
clean[k] = v
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return sdk.New${Name}SDK(map[string]any{
|
|
285
|
+
"apikey": cleanCanary["apikey"],
|
|
286
|
+
"secret": cleanCanary["secret"],
|
|
287
|
+
"headers": map[string]any{"X-Custom-Token": cleanCanary["header"]},
|
|
288
|
+
"clean": clean,
|
|
289
|
+
"feature": feature,
|
|
290
|
+
"extend": append([]any{newCleanCapture(sinks)}, extra...),
|
|
291
|
+
"utility": map[string]any{
|
|
292
|
+
"fetcher": sdk.FetcherFunc(func(_ *sdk.Context, url string, fetchdef map[string]any) (any, error) {
|
|
293
|
+
return scenario.respond(url, fetchdef)
|
|
294
|
+
}),
|
|
295
|
+
},
|
|
296
|
+
})
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// One operation this SDK can perform: the client method returning the
|
|
300
|
+
// entity, the op method on it, and the match it completes with.
|
|
301
|
+
type cleanOp struct {
|
|
302
|
+
accessor string
|
|
303
|
+
method string
|
|
304
|
+
match map[string]any
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
func cleanEntity(client *sdk.${Name}SDK, accessor string) reflect.Value {
|
|
308
|
+
return reflect.ValueOf(client).MethodByName(accessor).
|
|
309
|
+
Call([]reflect.Value{reflect.ValueOf(map[string]any(nil))})[0]
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
func cleanInvoke(client *sdk.${Name}SDK, op cleanOp, ctrl map[string]any) (any, error) {
|
|
313
|
+
match := map[string]any{}
|
|
314
|
+
for k, v := range op.match {
|
|
315
|
+
match[k] = v
|
|
316
|
+
}
|
|
317
|
+
rets := cleanEntity(client, op.accessor).MethodByName(op.method).Call([]reflect.Value{
|
|
318
|
+
reflect.ValueOf(match),
|
|
319
|
+
reflect.ValueOf(ctrl),
|
|
320
|
+
})
|
|
321
|
+
var err error
|
|
322
|
+
if e, ok := rets[1].Interface().(error); ok {
|
|
323
|
+
err = e
|
|
324
|
+
}
|
|
325
|
+
return rets[0].Interface(), err
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Every path parameter an op's points declare, filled in.
|
|
329
|
+
func cleanFilled(client *sdk.${Name}SDK, entity string, method string) map[string]any {
|
|
330
|
+
filled := map[string]any{}
|
|
331
|
+
opdef := core.ToMapAny(core.ToMapAny(core.ToMapAny(core.ToMapAny(
|
|
332
|
+
client.GetRootCtx().Config["entity"])[entity])["op"])[strings.ToLower(method)])
|
|
333
|
+
points, _ := opdef["points"].([]any)
|
|
334
|
+
for _, point := range points {
|
|
335
|
+
params, _ := core.ToMapAny(core.ToMapAny(point)["args"])["params"].([]any)
|
|
336
|
+
for _, p := range params {
|
|
337
|
+
if name, ok := core.ToMapAny(p)["name"].(string); ok {
|
|
338
|
+
filled[name] = "p1"
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return filled
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// The first operation that completes against a plain 200: with no
|
|
346
|
+
// arguments, else with every path parameter its points declare filled in.
|
|
347
|
+
// An entity accessor is a client method taking one options map and
|
|
348
|
+
// returning something that answers GetName().
|
|
349
|
+
func cleanUsableOp() (cleanOp, bool) {
|
|
350
|
+
plain := func() *sdk.${Name}SDK {
|
|
351
|
+
return sdk.New${Name}SDK(map[string]any{
|
|
352
|
+
"apikey": cleanCanary["apikey"],
|
|
353
|
+
"utility": map[string]any{
|
|
354
|
+
"fetcher": sdk.FetcherFunc(func(*sdk.Context, string, map[string]any) (any, error) {
|
|
355
|
+
return cleanResponse(200, map[string]any{"id": "i1"}, nil), nil
|
|
356
|
+
}),
|
|
357
|
+
},
|
|
358
|
+
})
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
probe := plain()
|
|
362
|
+
cv := reflect.ValueOf(probe)
|
|
363
|
+
ct := cv.Type()
|
|
364
|
+
mapType := reflect.TypeOf(map[string]any{})
|
|
365
|
+
|
|
366
|
+
accessors := []string{}
|
|
367
|
+
for i := 0; i < ct.NumMethod(); i++ {
|
|
368
|
+
m := ct.Method(i)
|
|
369
|
+
if m.Type.NumIn() != 2 || m.Type.NumOut() != 1 || m.Type.In(1) != mapType {
|
|
370
|
+
continue
|
|
371
|
+
}
|
|
372
|
+
ent := cleanEntity(probe, m.Name)
|
|
373
|
+
if !ent.IsValid() || (ent.Kind() == reflect.Ptr && ent.IsNil()) {
|
|
374
|
+
continue
|
|
375
|
+
}
|
|
376
|
+
gn := ent.MethodByName("GetName")
|
|
377
|
+
if !gn.IsValid() || gn.Type().NumIn() != 0 || gn.Type().NumOut() != 1 ||
|
|
378
|
+
gn.Type().Out(0).Kind() != reflect.String {
|
|
379
|
+
continue
|
|
380
|
+
}
|
|
381
|
+
accessors = append(accessors, m.Name)
|
|
382
|
+
}
|
|
383
|
+
sort.Strings(accessors)
|
|
384
|
+
|
|
385
|
+
for _, accessor := range accessors {
|
|
386
|
+
for _, method := range []string{"List", "Load", "Create", "Update", "Remove"} {
|
|
387
|
+
om := cleanEntity(probe, accessor).MethodByName(method)
|
|
388
|
+
if !om.IsValid() || om.Type().NumIn() != 2 || om.Type().NumOut() != 2 {
|
|
389
|
+
continue
|
|
390
|
+
}
|
|
391
|
+
name := cleanEntity(probe, accessor).MethodByName("GetName").
|
|
392
|
+
Call(nil)[0].String()
|
|
393
|
+
for _, match := range []map[string]any{{}, cleanFilled(probe, name, method)} {
|
|
394
|
+
op := cleanOp{accessor: accessor, method: method, match: match}
|
|
395
|
+
if _, err := cleanInvoke(plain(), op, map[string]any{}); err == nil {
|
|
396
|
+
return op, true
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return cleanOp{}, false
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
func cleanDrive(client *sdk.${Name}SDK, op cleanOp, ctrl map[string]any, sinks *[]cleanSink) error {
|
|
405
|
+
// A caller may keep the record it passed rather than read ctrl["explain"].
|
|
406
|
+
held, _ := ctrl["explain"].(map[string]any)
|
|
407
|
+
out, err := cleanInvoke(client, op, ctrl)
|
|
408
|
+
if err != nil {
|
|
409
|
+
*sinks = append(*sinks, cleanSurfaces("error", err)...)
|
|
410
|
+
}
|
|
411
|
+
if out != nil {
|
|
412
|
+
*sinks = append(*sinks, cleanSurfaces("result", out)...)
|
|
413
|
+
}
|
|
414
|
+
explain, ok := ctrl["explain"].(map[string]any)
|
|
415
|
+
if ok {
|
|
416
|
+
*sinks = append(*sinks, cleanSurfaces("explain", explain)...)
|
|
417
|
+
}
|
|
418
|
+
if held != nil && reflect.ValueOf(held).Pointer() != reflect.ValueOf(explain).Pointer() {
|
|
419
|
+
*sinks = append(*sinks, cleanSurfaces("explain:held", held)...)
|
|
420
|
+
}
|
|
421
|
+
return err
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// A feature that fails from inside the pipeline, quoting the request it
|
|
425
|
+
// saw: a panic MakeError never handled. Its PreUnexpected variant panics
|
|
426
|
+
// inside MakeError, after the cleaning there.
|
|
427
|
+
type cleanThrow struct {
|
|
428
|
+
sdk.BaseFeature
|
|
429
|
+
response bool
|
|
430
|
+
unexpected bool
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
func newCleanThrow(response bool, unexpected bool) *cleanThrow {
|
|
434
|
+
return &cleanThrow{
|
|
435
|
+
BaseFeature: sdk.BaseFeature{Version: "0.0.1", Name: "throwhook", Active: true},
|
|
436
|
+
response: response,
|
|
437
|
+
unexpected: unexpected,
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
func (f *cleanThrow) PreResponse(ctx *sdk.Context) {
|
|
442
|
+
if f.response {
|
|
443
|
+
raw, _ := json.Marshal(ctx.Spec)
|
|
444
|
+
panic(fmt.Errorf("hook saw %s", raw))
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
func (f *cleanThrow) PreUnexpected(ctx *sdk.Context) {
|
|
449
|
+
if f.unexpected {
|
|
450
|
+
raw, _ := json.Marshal(ctx.Spec)
|
|
451
|
+
panic(fmt.Errorf("hook saw %s", raw))
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// A stream that succeeds, so Stream never reaches Done.
|
|
456
|
+
type cleanStreamOk struct {
|
|
457
|
+
sdk.BaseFeature
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
func (f *cleanStreamOk) PreDone(ctx *sdk.Context) {
|
|
461
|
+
var items []any
|
|
462
|
+
switch d := ctx.Result.Resdata.(type) {
|
|
463
|
+
case []any:
|
|
464
|
+
items = d
|
|
465
|
+
case nil:
|
|
466
|
+
default:
|
|
467
|
+
items = []any{d}
|
|
468
|
+
}
|
|
469
|
+
ctx.Result.Stream = func() <-chan any {
|
|
470
|
+
ch := make(chan any, len(items))
|
|
471
|
+
for _, item := range items {
|
|
472
|
+
ch <- item
|
|
473
|
+
}
|
|
474
|
+
close(ch)
|
|
475
|
+
return ch
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// A stream function that panics when Stream calls it, quoting a credential.
|
|
480
|
+
type cleanStreamPanic struct {
|
|
481
|
+
sdk.BaseFeature
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
func (f *cleanStreamPanic) PreDone(ctx *sdk.Context) {
|
|
485
|
+
ctx.Result.Stream = func() <-chan any {
|
|
486
|
+
panic("stream saw " + cleanCanary["apikey"])
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// A panic that escapes is a surface too: what a crash would print.
|
|
491
|
+
func cleanCatch(name string, sinks *[]cleanSink, fn func() error) (err error) {
|
|
492
|
+
defer func() {
|
|
493
|
+
if r := recover(); r != nil {
|
|
494
|
+
*sinks = append(*sinks, cleanSurfaces(name+":panic", r)...)
|
|
495
|
+
err = fmt.Errorf("panic: %v", r)
|
|
496
|
+
}
|
|
497
|
+
}()
|
|
498
|
+
return fn()
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const cleanNoOp = "no operation of this SDK completes against a plain 200; nothing to sweep"
|
|
502
|
+
|
|
503
|
+
func TestCleanSweep(t *testing.T) {
|
|
504
|
+
op, found := cleanUsableOp()
|
|
505
|
+
if !found {
|
|
506
|
+
t.Skip(cleanNoOp)
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
sinks := []cleanSink{}
|
|
510
|
+
errs := map[string]error{}
|
|
511
|
+
explains := map[string]map[string]any{}
|
|
512
|
+
|
|
513
|
+
variants := []struct {
|
|
514
|
+
name string
|
|
515
|
+
ctrl func() map[string]any
|
|
516
|
+
}{
|
|
517
|
+
{"throw", func() map[string]any { return map[string]any{} }},
|
|
518
|
+
{"explain", func() map[string]any { return map[string]any{"explain": map[string]any{}} }},
|
|
519
|
+
{"nothrow", func() map[string]any {
|
|
520
|
+
return map[string]any{"throw": false, "explain": map[string]any{}}
|
|
521
|
+
}},
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
for _, scenario := range cleanScenarios {
|
|
525
|
+
for _, variant := range variants {
|
|
526
|
+
client := cleanMakeSdk(scenario, &sinks, nil)
|
|
527
|
+
ctrl := variant.ctrl()
|
|
528
|
+
err := cleanDrive(client, op, ctrl, &sinks)
|
|
529
|
+
key := scenario.name + "/" + variant.name
|
|
530
|
+
if err != nil {
|
|
531
|
+
errs[key] = err
|
|
532
|
+
}
|
|
533
|
+
if explain, ok := ctrl["explain"].(map[string]any); ok {
|
|
534
|
+
explains[key] = explain
|
|
535
|
+
}
|
|
536
|
+
sinks = append(sinks, cleanSurfaces("sdk", client)...)
|
|
537
|
+
sinks = append(sinks, cleanSink{"sdk:value", fmt.Sprintf("%+v", *client)})
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// A credential mistyped as a map. The go validator substitutes the
|
|
542
|
+
// default rather than rejecting it, so there is no rejection to sweep:
|
|
543
|
+
// sweep what the constructor produced, and what clean makes of the value
|
|
544
|
+
// should anything later quote it.
|
|
545
|
+
cleanCatch("mistyped", &sinks, func() error {
|
|
546
|
+
client := sdk.New${Name}SDK(map[string]any{
|
|
547
|
+
"apikey": map[string]any{"value": cleanCanary["apikey"]},
|
|
548
|
+
"clean": map[string]any{"values": cleanCanary["value"]},
|
|
549
|
+
})
|
|
550
|
+
sinks = append(sinks, cleanSurfaces("mistyped", client)...)
|
|
551
|
+
sinks = append(sinks, cleanSurfaces("mistyped:quoted",
|
|
552
|
+
client.GetUtility().Clean(client.GetRootCtx(), "found map: "+cleanCanary["apikey"]))...)
|
|
553
|
+
return nil
|
|
554
|
+
})
|
|
555
|
+
|
|
556
|
+
// MakeError is the one place PreUnexpected fires, so the variant that
|
|
557
|
+
// panics there alone is driven through a 404 as well.
|
|
558
|
+
for _, hooked := range []struct {
|
|
559
|
+
scenario cleanScenario
|
|
560
|
+
hook *cleanThrow
|
|
561
|
+
}{
|
|
562
|
+
{cleanScenarios[0], newCleanThrow(true, false)},
|
|
563
|
+
{cleanScenarios[0], newCleanThrow(true, true)},
|
|
564
|
+
{cleanScenarios[1], newCleanThrow(false, true)},
|
|
565
|
+
} {
|
|
566
|
+
hookerr := cleanCatch("hook", &sinks, func() error {
|
|
567
|
+
return cleanDrive(cleanMakeSdk(hooked.scenario, &sinks, nil, hooked.hook), op,
|
|
568
|
+
map[string]any{"explain": map[string]any{}}, &sinks)
|
|
569
|
+
})
|
|
570
|
+
if hookerr == nil {
|
|
571
|
+
t.Errorf("the throwing hook should fail the operation")
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Stream has no error channel: a panic inside it must end the stream
|
|
576
|
+
// through MakeError, cleaned, and not crash the process. However it
|
|
577
|
+
// ends, the explain record the caller passed is left clean.
|
|
578
|
+
for _, streamed := range []struct {
|
|
579
|
+
name string
|
|
580
|
+
saw string
|
|
581
|
+
extra []any
|
|
582
|
+
}{
|
|
583
|
+
{"stream-hook", "hook saw", []any{newCleanThrow(true, false)}},
|
|
584
|
+
{"stream", "stream saw", []any{&cleanStreamPanic{
|
|
585
|
+
BaseFeature: sdk.BaseFeature{Version: "0.0.1", Name: "streampanic", Active: true}}}},
|
|
586
|
+
{"stream-ok", "", []any{&cleanStreamOk{
|
|
587
|
+
BaseFeature: sdk.BaseFeature{Version: "0.0.1", Name: "streamok", Active: true}}}},
|
|
588
|
+
{"stream-plain", "", nil},
|
|
589
|
+
} {
|
|
590
|
+
client := cleanMakeSdk(cleanScenarios[0], &sinks, nil, streamed.extra...)
|
|
591
|
+
reqmatch := map[string]any{}
|
|
592
|
+
for k, v := range op.match {
|
|
593
|
+
reqmatch[k] = v
|
|
594
|
+
}
|
|
595
|
+
explain := map[string]any{}
|
|
596
|
+
items := cleanEntity(client, op.accessor).MethodByName("Stream").Call([]reflect.Value{
|
|
597
|
+
reflect.ValueOf(strings.ToLower(op.method)),
|
|
598
|
+
reflect.ValueOf(map[string]any{"reqmatch": reqmatch}),
|
|
599
|
+
reflect.ValueOf(map[string]any{"ctrl": map[string]any{"explain": explain}}),
|
|
600
|
+
})[0].Interface().(<-chan any)
|
|
601
|
+
for range items {
|
|
602
|
+
}
|
|
603
|
+
sinks = append(sinks, cleanSurfaces(streamed.name+":explain", explain)...)
|
|
604
|
+
if 0 == len(explain) {
|
|
605
|
+
t.Errorf("%s: the explain record was not filled", streamed.name)
|
|
606
|
+
}
|
|
607
|
+
msg, _ := core.ToMapAny(explain["err"])["message"].(string)
|
|
608
|
+
if ("" == streamed.saw) != (nil == explain["err"]) || !strings.Contains(msg, streamed.saw) {
|
|
609
|
+
t.Errorf("%s: only a failing stream ends as the SDK error, got %v", streamed.name, explain)
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// A registered value used as a property name is masked; names that mask
|
|
614
|
+
// alike are kept apart.
|
|
615
|
+
probe := cleanMakeSdk(cleanScenarios[0], &sinks, nil)
|
|
616
|
+
named, _ := probe.GetUtility().Clean(probe.GetRootCtx(), map[string]any{
|
|
617
|
+
cleanCanary["value"]: 1, cleanCanary["header"]: 2, "plain": 3,
|
|
618
|
+
}).(map[string]any)
|
|
619
|
+
sinks = append(sinks, cleanSurfaces("named", named)...)
|
|
620
|
+
if want := map[string]any{cleanMask: 2, cleanMask + "#1": 1, "plain": 3}; !reflect.DeepEqual(named, want) {
|
|
621
|
+
t.Errorf("property names: expected %v, got %v", want, named)
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// The generated config's own clean block is read beside the caller's,
|
|
625
|
+
// and is not changed by it.
|
|
626
|
+
cfgclean := map[string]any{"keys": "zzsens", "values": cleanCanary["config"]}
|
|
627
|
+
util := probe.GetUtility()
|
|
628
|
+
cfgctx := &core.Context{Options: util.MakeOptions(&core.Context{
|
|
629
|
+
Utility: util,
|
|
630
|
+
Config: map[string]any{"options": map[string]any{"clean": cfgclean}},
|
|
631
|
+
Options: map[string]any{"clean": map[string]any{"values": cleanCanary["value"]}},
|
|
632
|
+
})}
|
|
633
|
+
seeded, _ := util.Clean(cfgctx, "config "+cleanCanary["config"]+" caller "+cleanCanary["value"]).(string)
|
|
634
|
+
sinks = append(sinks, cleanSink{"config-clean", seeded})
|
|
635
|
+
if want := "config " + cleanMask + " caller " + cleanMask; seeded != want {
|
|
636
|
+
t.Errorf("config clean values: expected %q, got %q", want, seeded)
|
|
637
|
+
}
|
|
638
|
+
bykey, _ := util.Clean(cfgctx, map[string]any{"my_zzsens": "x", "other": "y"}).(map[string]any)
|
|
639
|
+
if want := map[string]any{"my_zzsens": cleanMask, "other": "y"}; !reflect.DeepEqual(bykey, want) {
|
|
640
|
+
t.Errorf("config clean keys: expected %v, got %v", want, bykey)
|
|
641
|
+
}
|
|
642
|
+
if cfgclean["keys"] != "zzsens" || cfgclean["values"] != cleanCanary["config"] {
|
|
643
|
+
t.Errorf("the config's own clean block was changed: %v", cfgclean)
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// With no clean option at all, the schema defaults still apply.
|
|
647
|
+
bare := sdk.New${Name}SDK(map[string]any{
|
|
648
|
+
"apikey": cleanCanary["apikey"],
|
|
649
|
+
"secret": cleanCanary["secret"],
|
|
650
|
+
"headers": map[string]any{"X-Custom-Token": cleanCanary["header"]},
|
|
651
|
+
"utility": map[string]any{
|
|
652
|
+
"fetcher": sdk.FetcherFunc(func(_ *sdk.Context, url string, fetchdef map[string]any) (any, error) {
|
|
653
|
+
return cleanScenarios[1].respond(url, fetchdef)
|
|
654
|
+
}),
|
|
655
|
+
},
|
|
656
|
+
})
|
|
657
|
+
if nil == cleanDrive(bare, op, map[string]any{"explain": map[string]any{}}, &sinks) {
|
|
658
|
+
t.Errorf("the 404 scenario should fail without a clean option too")
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// A feature's name is not a field name: only the sensitive names inside
|
|
662
|
+
// its settings register. An entity block, of per-entity settings or seeded
|
|
663
|
+
// records keyed by entity name and id, is not read at all.
|
|
664
|
+
featured := sdk.New${Name}SDK(map[string]any{
|
|
665
|
+
"apikey": cleanCanary["apikey"],
|
|
666
|
+
"feature": map[string]any{
|
|
667
|
+
"zzsecrets": map[string]any{"active": false, "kind": "PLAINSETTING-q8w2e4r6"},
|
|
668
|
+
"zzfeat": map[string]any{"active": false, "apitoken": "FEATTOKEN-z9y8x7w6"},
|
|
669
|
+
"test": map[string]any{"active": false, "entity": map[string]any{
|
|
670
|
+
"zztoken": map[string]any{"ZZTOKEN01": map[string]any{"note": "PLAINRECORD-t5r3e1w9"}},
|
|
671
|
+
}},
|
|
672
|
+
},
|
|
673
|
+
"entity": map[string]any{"zztoken": map[string]any{"alias": map[string]any{"zzkey": "PLAINALIAS-m2n4b6v8"}}},
|
|
674
|
+
})
|
|
675
|
+
fclean := featured.GetUtility().Clean
|
|
676
|
+
if got, _ := fclean(featured.GetRootCtx(), "kind PLAINSETTING-q8w2e4r6").(string); got != "kind PLAINSETTING-q8w2e4r6" {
|
|
677
|
+
t.Errorf("a setting of a feature named like a secret was registered: %q", got)
|
|
678
|
+
}
|
|
679
|
+
if got, _ := fclean(featured.GetRootCtx(), "token FEATTOKEN-z9y8x7w6").(string); got != "token "+cleanMask {
|
|
680
|
+
t.Errorf("a sensitive setting inside a feature was not registered: %q", got)
|
|
681
|
+
}
|
|
682
|
+
for _, plain := range []string{"record PLAINRECORD-t5r3e1w9", "alias PLAINALIAS-m2n4b6v8"} {
|
|
683
|
+
if got, _ := fclean(featured.GetRootCtx(), plain).(string); got != plain {
|
|
684
|
+
t.Errorf("an entity block was registered: %q", got)
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
leaked := []string{}
|
|
689
|
+
for _, s := range sinks {
|
|
690
|
+
if found := cleanLeaks(s.text); 0 < len(found) {
|
|
691
|
+
leaked = append(leaked, s.name+" ["+strings.Join(found, ", ")+"]")
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
fmt.Printf("clean: swept %d surface(s), %d leak(s)\\n", len(sinks), len(leaked))
|
|
696
|
+
|
|
697
|
+
if 0 < len(leaked) {
|
|
698
|
+
t.Fatalf("credential leaked through: %s", strings.Join(leaked, "; "))
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// The positive half: the slot the credential travelled in is masked,
|
|
702
|
+
// and an unregistered token in a response header is masked by name.
|
|
703
|
+
notfound, _ := errs["notfound/throw"].(*sdk.${Name}Error)
|
|
704
|
+
if notfound == nil {
|
|
705
|
+
t.Fatalf("the 404 scenario must return the SDK error, got %v", errs["notfound/throw"])
|
|
706
|
+
}
|
|
707
|
+
result, _ := notfound.Result.(map[string]any)
|
|
708
|
+
if core.ToInt(result["status"]) != 404 {
|
|
709
|
+
t.Fatalf("expected status 404 on the error, got %v", result["status"])
|
|
710
|
+
}
|
|
711
|
+
spec, _ := notfound.Spec.(map[string]any)
|
|
712
|
+
if !cleanAuth.suppressed {
|
|
713
|
+
switch cleanAuth.where {
|
|
714
|
+
case "query":
|
|
715
|
+
if got := cleanHeader(spec["query"], cleanAuth.name); got != cleanMask {
|
|
716
|
+
t.Errorf("%s: expected the mask, got %v", cleanAuth.name, got)
|
|
717
|
+
}
|
|
718
|
+
case "cookie":
|
|
719
|
+
got := fmt.Sprintf("%v", cleanHeader(spec["headers"], "cookie"))
|
|
720
|
+
if !strings.Contains(got, cleanMask) {
|
|
721
|
+
t.Errorf("cookie: expected the mask, got %v", got)
|
|
722
|
+
}
|
|
723
|
+
default:
|
|
724
|
+
got := fmt.Sprintf("%v", cleanHeader(spec["headers"], cleanAuth.name))
|
|
725
|
+
if !strings.HasSuffix(got, cleanMask) {
|
|
726
|
+
t.Errorf("%s: expected the mask, got %v", cleanAuth.name, got)
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if got := cleanHeader(spec["headers"], "x-custom-token"); got != cleanMask {
|
|
731
|
+
t.Errorf("x-custom-token: expected the mask, got %v", got)
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
coded, _ := errs["coded/throw"].(*sdk.${Name}Error)
|
|
735
|
+
if coded == nil || coded.Code != "denied_"+cleanMask {
|
|
736
|
+
t.Errorf("the coded error keeps its code, masked: got %#v", errs["coded/throw"])
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
explained := explains["ok/explain"]
|
|
740
|
+
explainedResult, _ := explained["result"].(map[string]any)
|
|
741
|
+
if explainedResult == nil {
|
|
742
|
+
t.Fatalf("the explain record should carry the result, got %v", explained)
|
|
743
|
+
}
|
|
744
|
+
if got := cleanHeader(explainedResult["headers"], "x-session-token"); got != cleanMask {
|
|
745
|
+
t.Errorf("x-session-token: expected the mask, got %v", got)
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
func TestCleanSensitivity(t *testing.T) {
|
|
750
|
+
op, found := cleanUsableOp()
|
|
751
|
+
if !found {
|
|
752
|
+
t.Skip(cleanNoOp)
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
sinks := []cleanSink{}
|
|
756
|
+
client := cleanMakeSdk(cleanScenarios[1], &sinks, map[string]any{"active": false})
|
|
757
|
+
err := cleanDrive(client, op, map[string]any{}, &sinks)
|
|
758
|
+
if err == nil {
|
|
759
|
+
t.Fatal("the 404 scenario must fail")
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// Explaining a failure must not cost it its error, nor the record.
|
|
763
|
+
discard := []cleanSink{}
|
|
764
|
+
ectrl := map[string]any{"explain": map[string]any{}}
|
|
765
|
+
explained := cleanDrive(cleanMakeSdk(cleanScenarios[1], &discard, map[string]any{"active": false}),
|
|
766
|
+
op, ectrl, &discard)
|
|
767
|
+
if explained == nil || explained.Error() != err.Error() {
|
|
768
|
+
t.Errorf("with clean off, explain lost the error: %v", explained)
|
|
769
|
+
}
|
|
770
|
+
if nil == ectrl["explain"].(map[string]any)["err"] {
|
|
771
|
+
t.Errorf("with clean off, the explain record was emptied: %v", ectrl["explain"])
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
leaked := 0
|
|
775
|
+
for _, s := range sinks {
|
|
776
|
+
if 0 < len(cleanLeaks(s.text)) {
|
|
777
|
+
leaked++
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
if leaked == 0 {
|
|
781
|
+
t.Fatal("with clean off, nothing showed the canary: the sweep is blind")
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if !cleanAuth.suppressed {
|
|
785
|
+
sdkerr, _ := err.(*sdk.${Name}Error)
|
|
786
|
+
if sdkerr == nil {
|
|
787
|
+
t.Fatalf("expected the SDK error, got %v", err)
|
|
788
|
+
}
|
|
789
|
+
raw, _ := json.Marshal(sdkerr.Spec)
|
|
790
|
+
text := string(raw)
|
|
791
|
+
composite := base64.StdEncoding.EncodeToString(
|
|
792
|
+
[]byte(cleanCanary["apikey"] + ":" + cleanCanary["secret"]))
|
|
793
|
+
if !strings.Contains(text, cleanCanary["apikey"]) && !strings.Contains(text, composite) {
|
|
794
|
+
t.Errorf("the raw spec should carry the credential when clean is off, got %s", text)
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
`
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
|
|
802
|
+
export {
|
|
803
|
+
TestClean
|
|
804
|
+
}
|