@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,809 @@
|
|
|
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, as TestClean_ts emits it: every credential slot holds a
|
|
13
|
+
// distinctive value, every diagnostic feature this SDK ships is switched on
|
|
14
|
+
// with a capturing sink, a real operation runs through every outcome, and
|
|
15
|
+
// every string that leaves the SDK is searched for the canaries and their
|
|
16
|
+
// encoded forms. With clean switched off the canary MUST show.
|
|
17
|
+
const TestClean = cmp(function TestClean(props: any) {
|
|
18
|
+
const { model } = props.ctx$
|
|
19
|
+
const { target } = 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: 'CleanTest.' + target.ext }, () => Content(render(model.const.Name, auth)))
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
function phpstr(s: string): string {
|
|
34
|
+
return "'" + String(s).replace(/\\/g, '\\\\').replace(/'/g, "\\'") + "'"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
function render(Name: string, auth: {
|
|
39
|
+
suppressed: boolean, where: string, name: string, basic: boolean
|
|
40
|
+
}): string {
|
|
41
|
+
const lower = Name.toLowerCase()
|
|
42
|
+
|
|
43
|
+
return `<?php
|
|
44
|
+
declare(strict_types=1);
|
|
45
|
+
|
|
46
|
+
// ${Name} SDK clean test
|
|
47
|
+
//
|
|
48
|
+
// The canary sweep: every credential slot holds a distinctive value, every
|
|
49
|
+
// diagnostic feature this SDK ships is switched on with a capturing sink, a
|
|
50
|
+
// real operation runs through every outcome, and every string that leaves
|
|
51
|
+
// the SDK is searched for the canaries and their encoded forms. It also
|
|
52
|
+
// proves its own sensitivity: with clean switched off the canary MUST show.
|
|
53
|
+
|
|
54
|
+
require_once __DIR__ . '/../${lower}_sdk.php';
|
|
55
|
+
|
|
56
|
+
use PHPUnit\\Framework\\TestCase;
|
|
57
|
+
|
|
58
|
+
class CleanTest extends TestCase
|
|
59
|
+
{
|
|
60
|
+
// Generated: the credential's wire placement is fixed when the SDK is
|
|
61
|
+
// built.
|
|
62
|
+
private const AUTH = [
|
|
63
|
+
'suppressed' => ${auth.suppressed ? 'true' : 'false'},
|
|
64
|
+
'where' => ${phpstr(auth.where)},
|
|
65
|
+
'name' => ${phpstr(auth.name)},
|
|
66
|
+
'basic' => ${auth.basic ? 'true' : 'false'},
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
private const CANARY = [
|
|
70
|
+
'apikey' => 'CANARY-APIKEY-k9x2m7q4p1',
|
|
71
|
+
'secret' => 'CANARY-SECRET-w3e8r5t2y6',
|
|
72
|
+
'header' => 'CANARY-HEADER-z1x4c7v0b3',
|
|
73
|
+
'value' => 'CANARY-VALUE-n5m8b2v9c4',
|
|
74
|
+
'config' => 'CANARY-CONFIG-h6j3k8l2m5',
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
private const MASK = '[redacted]';
|
|
78
|
+
|
|
79
|
+
// Every form a canary can travel in.
|
|
80
|
+
private static function forms(): array
|
|
81
|
+
{
|
|
82
|
+
$out = [];
|
|
83
|
+
foreach (self::CANARY as $v) {
|
|
84
|
+
$out[] = $v;
|
|
85
|
+
$out[] = base64_encode($v);
|
|
86
|
+
$out[] = rawurlencode($v);
|
|
87
|
+
}
|
|
88
|
+
$out[] = base64_encode(self::CANARY['apikey'] . ':' . self::CANARY['secret']);
|
|
89
|
+
return $out;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Header maps keep the caller's spelling; the assertion should not care.
|
|
93
|
+
private static function header(mixed $map, string $name): mixed
|
|
94
|
+
{
|
|
95
|
+
if (is_object($map)) {
|
|
96
|
+
$map = get_object_vars($map);
|
|
97
|
+
}
|
|
98
|
+
if (!is_array($map)) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
foreach ($map as $k => $v) {
|
|
102
|
+
if (strtolower((string)$k) === strtolower($name)) {
|
|
103
|
+
return $v;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private static function leaks(string $text): array
|
|
110
|
+
{
|
|
111
|
+
$found = [];
|
|
112
|
+
foreach (self::forms() as $f) {
|
|
113
|
+
if (str_contains($text, $f)) {
|
|
114
|
+
$found[] = $f;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return $found;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Every way a caller renders a value: json and print_r for all of them,
|
|
121
|
+
// var_export where it walks the whole graph (a record, an error), and
|
|
122
|
+
// the error's own message and string form.
|
|
123
|
+
public static function surfaces(string $name, mixed $val): array
|
|
124
|
+
{
|
|
125
|
+
// Xdebug's develop mode writes every frame's arguments onto the
|
|
126
|
+
// exception at the throw; that is a debugger's view, like err.ctx.
|
|
127
|
+
for ($e = $val instanceof \\Throwable ? $val : null; null !== $e; $e = $e->getPrevious()) {
|
|
128
|
+
if (property_exists($e, 'xdebug_message')) {
|
|
129
|
+
unset($e->xdebug_message);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
$out = [];
|
|
134
|
+
$push = function (string $kind, callable $fn) use (&$out, $name): void {
|
|
135
|
+
try {
|
|
136
|
+
$text = $fn();
|
|
137
|
+
if (is_string($text)) {
|
|
138
|
+
$out[] = ['name' => $name . ':' . $kind, 'text' => $text];
|
|
139
|
+
}
|
|
140
|
+
} catch (\\Throwable $_e) {
|
|
141
|
+
// A renderer that refuses the value is not a surface.
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
$push('json', fn () => json_encode($val, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
|
|
145
|
+
$push('print_r', fn () => print_r($val, true));
|
|
146
|
+
if (is_array($val) || $val instanceof \\Throwable) {
|
|
147
|
+
$push('var_export', fn () => var_export($val, true));
|
|
148
|
+
}
|
|
149
|
+
if ($val instanceof \\Throwable) {
|
|
150
|
+
$push('message', fn () => $val->getMessage());
|
|
151
|
+
$push('string', fn () => (string)$val);
|
|
152
|
+
}
|
|
153
|
+
return $out;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private static function response(int $status, mixed $data, array $headers = []): array
|
|
157
|
+
{
|
|
158
|
+
$h = ['content-type' => 'application/json'];
|
|
159
|
+
foreach ($headers as $k => $v) {
|
|
160
|
+
$h[strtolower((string)$k)] = $v;
|
|
161
|
+
}
|
|
162
|
+
return [
|
|
163
|
+
'status' => $status,
|
|
164
|
+
'statusText' => $status < 400 ? 'OK' : 'ERR',
|
|
165
|
+
'headers' => $h,
|
|
166
|
+
'json' => function () use ($data) { return $data; },
|
|
167
|
+
'body' => json_encode($data),
|
|
168
|
+
];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Each scenario is a transport answering the [response, err] pair the
|
|
172
|
+
// fetcher contract names.
|
|
173
|
+
private static function scenarios(): array
|
|
174
|
+
{
|
|
175
|
+
return [
|
|
176
|
+
'ok' => function (string $_url, array $_fetchdef): array {
|
|
177
|
+
return [self::response(200, ['id' => 'i1', 'name' => 'n1'],
|
|
178
|
+
['x-session-token' => 'RESP-TOKEN-a1b2c3d4e5']), null];
|
|
179
|
+
},
|
|
180
|
+
'notfound' => function (string $_url, array $_fetchdef): array {
|
|
181
|
+
return [self::response(404, ['error' => 'no such record']), null];
|
|
182
|
+
},
|
|
183
|
+
'server' => function (string $_url, array $_fetchdef): array {
|
|
184
|
+
return [self::response(500, ['error' => 'boom']), null];
|
|
185
|
+
},
|
|
186
|
+
// A transport failure quotes the URL, query credential included.
|
|
187
|
+
'transport' => function (string $url, array $_fetchdef): array {
|
|
188
|
+
return [null, new \\RuntimeException('socket hang up (URL was: "' . $url . '")')];
|
|
189
|
+
},
|
|
190
|
+
// The SDK's own error, its code quoting a registered value.
|
|
191
|
+
'coded' => function (string $_url, array $_fetchdef): array {
|
|
192
|
+
return [null, new ${Name}Error('denied_' . self::CANARY['apikey'], 'coded failure')];
|
|
193
|
+
},
|
|
194
|
+
// A body that is not JSON: the fetcher answers no parsed body.
|
|
195
|
+
'notjson' => function (string $_url, array $_fetchdef): array {
|
|
196
|
+
return [[
|
|
197
|
+
'status' => 200,
|
|
198
|
+
'statusText' => 'OK',
|
|
199
|
+
'headers' => [],
|
|
200
|
+
'json' => function () { return null; },
|
|
201
|
+
'body' => '<html>',
|
|
202
|
+
], null];
|
|
203
|
+
},
|
|
204
|
+
];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// True when this SDK was generated with the named feature.
|
|
208
|
+
private static function has_feature(string $name): bool
|
|
209
|
+
{
|
|
210
|
+
$config = ${Name}Config::shared_config();
|
|
211
|
+
$f = $config['feature'] ?? [];
|
|
212
|
+
return is_array($f) && isset($f[$name]);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Captures the serialised context from inside the pipeline: what a hook
|
|
216
|
+
// author would hand to a logger. It also keeps the operation context,
|
|
217
|
+
// the one place the explain record can be read back from.
|
|
218
|
+
// What the watchers capture, kept out of the SDK's object graph: with
|
|
219
|
+
// clean off, var_export of an error walks into the watcher, and text it
|
|
220
|
+
// held would be rendered again on every later capture.
|
|
221
|
+
public static array $captures = [];
|
|
222
|
+
|
|
223
|
+
private static function capture_feature(\\ArrayObject $sinks): ${Name}BaseFeature
|
|
224
|
+
{
|
|
225
|
+
$key = count(self::$captures);
|
|
226
|
+
self::$captures[$key] = $sinks;
|
|
227
|
+
return new class ($key) extends ${Name}BaseFeature {
|
|
228
|
+
public mixed $last = null;
|
|
229
|
+
|
|
230
|
+
public function __construct(private int $sinkkey)
|
|
231
|
+
{
|
|
232
|
+
parent::__construct();
|
|
233
|
+
$this->name = 'capture';
|
|
234
|
+
$this->version = '0.0.1';
|
|
235
|
+
$this->active = true;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private function take(string $name, mixed $val): void
|
|
239
|
+
{
|
|
240
|
+
$sinks = CleanTest::$captures[$this->sinkkey];
|
|
241
|
+
foreach (CleanTest::surfaces($name, $val) as $s) {
|
|
242
|
+
$sinks[] = $s;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
public function PrePoint(${Name}Context $ctx): void
|
|
247
|
+
{
|
|
248
|
+
$this->last = $ctx;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
public function PreRequest(${Name}Context $ctx): void
|
|
252
|
+
{
|
|
253
|
+
$this->take('ctx@PreRequest', $ctx);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
public function PreResponse(${Name}Context $ctx): void
|
|
257
|
+
{
|
|
258
|
+
$this->take('ctx@PreResponse', $ctx);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// The SDK's own error as a hook reads it, which an observability
|
|
262
|
+
// feature logs.
|
|
263
|
+
public function PreUnexpected(${Name}Context $ctx): void
|
|
264
|
+
{
|
|
265
|
+
$this->take('ctx@PreUnexpected', $ctx);
|
|
266
|
+
if ($ctx->ctrl->err instanceof ${Name}Error) {
|
|
267
|
+
$this->take('ctrl.err@PreUnexpected', $ctx->ctrl->err);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// A feature that throws from inside the pipeline, quoting the request
|
|
274
|
+
// it saw: an error make_error never handled.
|
|
275
|
+
// Its PreUnexpected variant throws where that hook fires: make_error.
|
|
276
|
+
private static function throw_feature(bool $response = true, bool $unexpected = false): ${Name}BaseFeature
|
|
277
|
+
{
|
|
278
|
+
return new class ($response, $unexpected) extends ${Name}BaseFeature {
|
|
279
|
+
public function __construct(private bool $response, private bool $unexpected)
|
|
280
|
+
{
|
|
281
|
+
parent::__construct();
|
|
282
|
+
$this->name = 'throwhook';
|
|
283
|
+
$this->version = '0.0.1';
|
|
284
|
+
$this->active = true;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
public function PreResponse(${Name}Context $ctx): void
|
|
288
|
+
{
|
|
289
|
+
if ($this->response) {
|
|
290
|
+
throw new \\RuntimeException('hook saw ' . json_encode($ctx->spec));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
public function PreUnexpected(${Name}Context $ctx): void
|
|
295
|
+
{
|
|
296
|
+
if ($this->unexpected) {
|
|
297
|
+
throw new \\RuntimeException('hook saw ' . json_encode($ctx->spec));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// A feature that throws a foreign exception whose string code, as some
|
|
304
|
+
// drivers set one, quotes a registered value.
|
|
305
|
+
private static function coded_throw_feature(string $failcode): ${Name}BaseFeature
|
|
306
|
+
{
|
|
307
|
+
return new class ($failcode) extends ${Name}BaseFeature {
|
|
308
|
+
public function __construct(private string $failcode)
|
|
309
|
+
{
|
|
310
|
+
parent::__construct();
|
|
311
|
+
$this->name = 'codedhook';
|
|
312
|
+
$this->version = '0.0.1';
|
|
313
|
+
$this->active = true;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
public function PreResponse(${Name}Context $ctx): void
|
|
317
|
+
{
|
|
318
|
+
throw new class ('coded hook failure', $this->failcode) extends \\RuntimeException {
|
|
319
|
+
public function __construct(string $msg, string $code)
|
|
320
|
+
{
|
|
321
|
+
parent::__construct($msg);
|
|
322
|
+
$this->code = $code;
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// A feature whose stream fails while the caller iterates it, quoting a
|
|
330
|
+
// credential.
|
|
331
|
+
private static function stream_throw_feature(string $key): ${Name}BaseFeature
|
|
332
|
+
{
|
|
333
|
+
return new class ($key) extends ${Name}BaseFeature {
|
|
334
|
+
public function __construct(private string $key)
|
|
335
|
+
{
|
|
336
|
+
parent::__construct();
|
|
337
|
+
$this->name = 'streamthrow';
|
|
338
|
+
$this->version = '0.0.1';
|
|
339
|
+
$this->active = true;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
public function PreDone(${Name}Context $ctx): void
|
|
343
|
+
{
|
|
344
|
+
$key = $this->key;
|
|
345
|
+
$ctx->result->stream = function () use ($key): \\Generator {
|
|
346
|
+
throw new \\RuntimeException('stream saw ' . $key);
|
|
347
|
+
yield null;
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// A stream that succeeds, so the pipeline's terminal step never runs.
|
|
354
|
+
private static function stream_ok_feature(): ${Name}BaseFeature
|
|
355
|
+
{
|
|
356
|
+
return new class () extends ${Name}BaseFeature {
|
|
357
|
+
public function __construct()
|
|
358
|
+
{
|
|
359
|
+
parent::__construct();
|
|
360
|
+
$this->name = 'streamok';
|
|
361
|
+
$this->version = '0.0.1';
|
|
362
|
+
$this->active = true;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
public function PreDone(${Name}Context $ctx): void
|
|
366
|
+
{
|
|
367
|
+
$data = $ctx->result->resdata;
|
|
368
|
+
$items = is_array($data) && array_is_list($data) ? $data : (null === $data ? [] : [$data]);
|
|
369
|
+
$ctx->result->stream = function () use ($items): \\Generator {
|
|
370
|
+
yield from $items;
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
private static function make_sdk(
|
|
377
|
+
callable $respond, \\ArrayObject $sinks, ?array $cleanopts = null, array $extra = []
|
|
378
|
+
): array
|
|
379
|
+
{
|
|
380
|
+
$capture = function (string $name) use ($sinks): callable {
|
|
381
|
+
return function (mixed $rec) use ($name, $sinks): void {
|
|
382
|
+
foreach (self::surfaces($name, $rec) as $s) {
|
|
383
|
+
$sinks[] = $s;
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
$feature = [];
|
|
389
|
+
if (self::has_feature('log')) {
|
|
390
|
+
$feature['log'] = ['active' => true, 'logger' => $capture('log')];
|
|
391
|
+
}
|
|
392
|
+
if (self::has_feature('debug')) {
|
|
393
|
+
$feature['debug'] = ['active' => true, 'onEntry' => $capture('debug')];
|
|
394
|
+
}
|
|
395
|
+
if (self::has_feature('audit')) {
|
|
396
|
+
$feature['audit'] = ['active' => true, 'sink' => $capture('audit')];
|
|
397
|
+
}
|
|
398
|
+
if (self::has_feature('telemetry')) {
|
|
399
|
+
$feature['telemetry'] = ['active' => true, 'exporter' => $capture('telemetry')];
|
|
400
|
+
}
|
|
401
|
+
if (self::has_feature('cost')) {
|
|
402
|
+
$feature['cost'] = ['active' => true, 'sink' => $capture('cost')];
|
|
403
|
+
}
|
|
404
|
+
if (self::has_feature('metrics')) {
|
|
405
|
+
$feature['metrics'] = ['active' => true];
|
|
406
|
+
}
|
|
407
|
+
if (self::has_feature('clienttrack')) {
|
|
408
|
+
$feature['clienttrack'] = ['active' => true];
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
$watcher = self::capture_feature($sinks);
|
|
412
|
+
|
|
413
|
+
$opts = [
|
|
414
|
+
'apikey' => self::CANARY['apikey'],
|
|
415
|
+
'secret' => self::CANARY['secret'],
|
|
416
|
+
'headers' => ['X-Custom-Token' => self::CANARY['header']],
|
|
417
|
+
'clean' => array_merge(['values' => self::CANARY['value']], $cleanopts ?? []),
|
|
418
|
+
'extend' => array_merge([$watcher], $extra),
|
|
419
|
+
'utility' => [
|
|
420
|
+
'fetcher' => function (${Name}Context $_ctx, string $url, array $fetchdef) use ($respond): array {
|
|
421
|
+
return $respond($url, $fetchdef);
|
|
422
|
+
},
|
|
423
|
+
],
|
|
424
|
+
];
|
|
425
|
+
if (0 < count($feature)) {
|
|
426
|
+
$opts['feature'] = $feature;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return [new ${Name}SDK($opts), $watcher];
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// The first operation that completes against a plain 200: with no
|
|
433
|
+
// arguments, else with every path parameter its points declare filled
|
|
434
|
+
// in. An entity accessor is a capitalised client method whose result
|
|
435
|
+
// answers get_name(), as the feature corpus runner finds them.
|
|
436
|
+
private static function usable_op(): ?array
|
|
437
|
+
{
|
|
438
|
+
$plain = new ${Name}SDK([
|
|
439
|
+
'apikey' => self::CANARY['apikey'],
|
|
440
|
+
'utility' => [
|
|
441
|
+
'fetcher' => function (${Name}Context $_ctx, string $_url, array $_fetchdef): array {
|
|
442
|
+
return [self::response(200, ['id' => 'i1']), null];
|
|
443
|
+
},
|
|
444
|
+
],
|
|
445
|
+
]);
|
|
446
|
+
|
|
447
|
+
$found = [];
|
|
448
|
+
foreach (get_class_methods($plain) as $m) {
|
|
449
|
+
if (!preg_match('/^[A-Z]/', $m)) {
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
try {
|
|
453
|
+
$ent = $plain->$m();
|
|
454
|
+
} catch (\\Throwable $_e) {
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
if (!is_object($ent) || !method_exists($ent, 'get_name')) {
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
$found[$ent->get_name()] = $m;
|
|
461
|
+
}
|
|
462
|
+
ksort($found);
|
|
463
|
+
|
|
464
|
+
$rank = ['list' => 0, 'load' => 1];
|
|
465
|
+
$entities = ${Name}Config::shared_config()['entity'] ?? [];
|
|
466
|
+
foreach ($found as $entname => $accessor) {
|
|
467
|
+
$ent = $plain->$accessor();
|
|
468
|
+
$ops = array_values(array_filter(['list', 'load', 'create', 'update', 'remove'],
|
|
469
|
+
function (string $op) use ($ent): bool {
|
|
470
|
+
return method_exists($ent, $op);
|
|
471
|
+
}));
|
|
472
|
+
usort($ops, function (string $a, string $b) use ($rank): int {
|
|
473
|
+
return ($rank[$a] ?? 2) <=> ($rank[$b] ?? 2);
|
|
474
|
+
});
|
|
475
|
+
foreach ($ops as $op) {
|
|
476
|
+
$filled = [];
|
|
477
|
+
foreach ((array)($entities[$entname]['op'][$op]['points'] ?? []) as $point) {
|
|
478
|
+
foreach ((array)($point['args']['params'] ?? []) as $p) {
|
|
479
|
+
if (is_string($p['name'] ?? null)) {
|
|
480
|
+
$filled[$p['name']] = 'p1';
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
foreach ([[], $filled] as $match) {
|
|
485
|
+
try {
|
|
486
|
+
$plain->$accessor()->$op($match, []);
|
|
487
|
+
return ['accessor' => $accessor, 'op' => $op, 'match' => $match];
|
|
488
|
+
} catch (\\Throwable $_e) {
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
private static function drive(object $sdk, object $watcher, array $target, array $ctrl, \\ArrayObject $sinks): array
|
|
498
|
+
{
|
|
499
|
+
// What the caller passed and keeps; an array, so the call cannot
|
|
500
|
+
// change it, but it is searched like the record the watcher reads.
|
|
501
|
+
$held = $ctrl['explain'] ?? null;
|
|
502
|
+
$out = null;
|
|
503
|
+
$err = null;
|
|
504
|
+
try {
|
|
505
|
+
$accessor = $target['accessor'];
|
|
506
|
+
$op = $target['op'];
|
|
507
|
+
$out = $sdk->$accessor()->$op($target['match'], $ctrl);
|
|
508
|
+
} catch (\\Throwable $e) {
|
|
509
|
+
$err = $e;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (null !== $err) {
|
|
513
|
+
foreach (self::surfaces('error', $err) as $s) {
|
|
514
|
+
$sinks[] = $s;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (null !== $out) {
|
|
518
|
+
foreach (self::surfaces('result', $out) as $s) {
|
|
519
|
+
$sinks[] = $s;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
$explain = null;
|
|
524
|
+
$last = $watcher->last;
|
|
525
|
+
if (null !== $last && is_array($last->ctrl->explain)) {
|
|
526
|
+
$explain = $last->ctrl->explain;
|
|
527
|
+
foreach (self::surfaces('explain', $explain) as $s) {
|
|
528
|
+
$sinks[] = $s;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if (null !== $held && $held !== $explain) {
|
|
532
|
+
foreach (self::surfaces('explain:held', $held) as $s) {
|
|
533
|
+
$sinks[] = $s;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return [$err, $explain];
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
public function test_no_credential_leaves_the_sdk_in_any_form(): void
|
|
541
|
+
{
|
|
542
|
+
// Frameworks turn every notice into an exception (PHPUnit 8 did too);
|
|
543
|
+
// one thrown mid-pipeline must still leave clean.
|
|
544
|
+
set_error_handler(static function (int $no, string $str, string $file, int $line): bool {
|
|
545
|
+
throw new \\ErrorException($str, 0, $no, $file, $line);
|
|
546
|
+
});
|
|
547
|
+
try {
|
|
548
|
+
$this->sweep();
|
|
549
|
+
} finally {
|
|
550
|
+
restore_error_handler();
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
private function sweep(): void
|
|
555
|
+
{
|
|
556
|
+
$target = self::usable_op();
|
|
557
|
+
if (null === $target) {
|
|
558
|
+
$this->markTestSkipped('no operation of this SDK completes against a plain 200; nothing to sweep');
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
$sinks = new \\ArrayObject();
|
|
562
|
+
$errors = [];
|
|
563
|
+
$explains = [];
|
|
564
|
+
|
|
565
|
+
$variants = [
|
|
566
|
+
'throw' => [],
|
|
567
|
+
'explain' => ['explain' => ['on' => true]],
|
|
568
|
+
'nothrow' => ['throw' => false, 'explain' => ['on' => true]],
|
|
569
|
+
];
|
|
570
|
+
|
|
571
|
+
foreach (self::scenarios() as $sname => $respond) {
|
|
572
|
+
foreach ($variants as $vname => $ctrl) {
|
|
573
|
+
[$sdk, $watcher] = self::make_sdk($respond, $sinks);
|
|
574
|
+
[$err, $explain] = self::drive($sdk, $watcher, $target, $ctrl, $sinks);
|
|
575
|
+
$key = $sname . '/' . $vname;
|
|
576
|
+
if (null !== $err) {
|
|
577
|
+
$errors[$key] = $err;
|
|
578
|
+
}
|
|
579
|
+
if (null !== $explain) {
|
|
580
|
+
$explains[$key] = $explain;
|
|
581
|
+
}
|
|
582
|
+
foreach (self::surfaces('sdk', $sdk) as $s) {
|
|
583
|
+
$sinks[] = $s;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// A credential mistyped as a map is rejected by validation, whose
|
|
589
|
+
// message quotes the value it rejected.
|
|
590
|
+
$rejected = null;
|
|
591
|
+
try {
|
|
592
|
+
new ${Name}SDK([
|
|
593
|
+
'apikey' => ['value' => self::CANARY['apikey']],
|
|
594
|
+
'clean' => ['values' => self::CANARY['value']],
|
|
595
|
+
]);
|
|
596
|
+
} catch (\\Throwable $e) {
|
|
597
|
+
$rejected = $e;
|
|
598
|
+
}
|
|
599
|
+
$this->assertNotNull($rejected, 'a credential mistyped as a map should be rejected');
|
|
600
|
+
foreach (self::surfaces('rejected', $rejected) as $s) {
|
|
601
|
+
$sinks[] = $s;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// An error a feature hook throws, quoting the request, skips make_error.
|
|
605
|
+
// PreUnexpected fires only in make_error, which the variant throwing
|
|
606
|
+
// there alone reaches through a 404.
|
|
607
|
+
foreach ([['ok', self::throw_feature()], ['ok', self::throw_feature(true, true)],
|
|
608
|
+
['notfound', self::throw_feature(false, true)]] as [$sname, $hook]) {
|
|
609
|
+
[$hooked, $hwatcher] = self::make_sdk(self::scenarios()[$sname], $sinks, null, [$hook]);
|
|
610
|
+
[$hookerr, $_hexplain] = self::drive($hooked, $hwatcher, $target, ['explain' => ['on' => true]], $sinks);
|
|
611
|
+
$this->assertNotNull($hookerr, 'the throwing hook should fail the operation');
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Iterating a stream runs inside the same catch path as the operation,
|
|
615
|
+
// and the explain record is cleaned however it ends. The caller's copy
|
|
616
|
+
// is its own, so the record is read back as the watcher saw it.
|
|
617
|
+
foreach ([['stream', [self::stream_throw_feature(self::CANARY['apikey'])]],
|
|
618
|
+
['stream-ok', [self::stream_ok_feature()]], ['stream-plain', []]] as [$name, $extra]) {
|
|
619
|
+
[$streamed, $swatcher] = self::make_sdk(self::scenarios()['ok'], $sinks, null, $extra);
|
|
620
|
+
$streamerr = null;
|
|
621
|
+
try {
|
|
622
|
+
$accessor = $target['accessor'];
|
|
623
|
+
foreach ($streamed->$accessor()->stream($target['op'], ['reqmatch' => $target['match']],
|
|
624
|
+
['ctrl' => ['explain' => ['on' => true]]]) as $_item) {
|
|
625
|
+
}
|
|
626
|
+
} catch (\\Throwable $e) {
|
|
627
|
+
$streamerr = $e;
|
|
628
|
+
}
|
|
629
|
+
$this->assertSame('stream' === $name, null !== $streamerr, $name . ': only the failing stream throws');
|
|
630
|
+
if (null !== $streamerr) {
|
|
631
|
+
foreach (self::surfaces($name, $streamerr) as $s) {
|
|
632
|
+
$sinks[] = $s;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
$explain = $swatcher->last->ctrl->explain ?? [];
|
|
636
|
+
$this->assertGreaterThan(1, count((array)$explain), $name . ': the explain record was not filled');
|
|
637
|
+
foreach (self::surfaces($name . ':explain', $explain) as $s) {
|
|
638
|
+
$sinks[] = $s;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// A registered value used as a property name is masked; names that
|
|
643
|
+
// mask alike are all kept.
|
|
644
|
+
$named = ($hooked->get_utility()->clean)($hooked->get_root_ctx(),
|
|
645
|
+
[self::CANARY['header'] => 1, self::CANARY['value'] => 2, 'plain' => 3]);
|
|
646
|
+
$this->assertSame([self::MASK => 1, self::MASK . '#1' => 2, 'plain' => 3], $named);
|
|
647
|
+
foreach (self::surfaces('named', $named) as $s) {
|
|
648
|
+
$sinks[] = $s;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// A foreign exception a hook throws, its string code quoting the key.
|
|
652
|
+
[$codedhook, $cwatcher] = self::make_sdk(self::scenarios()['ok'], $sinks, null,
|
|
653
|
+
[self::coded_throw_feature('denied_' . self::CANARY['apikey'])]);
|
|
654
|
+
[$codederr, $_cexplain] = self::drive($codedhook, $cwatcher, $target, [], $sinks);
|
|
655
|
+
$this->assertNotNull($codederr, 'the coded hook should fail the operation');
|
|
656
|
+
|
|
657
|
+
// The generated config's own clean block is read beside the caller's,
|
|
658
|
+
// and is not changed by it.
|
|
659
|
+
$config = ['options' => ['clean' => ['keys' => 'zzsens', 'values' => self::CANARY['config']]]];
|
|
660
|
+
$util = $hooked->get_utility();
|
|
661
|
+
$built = ($util->make_options)(new ${Name}Context([
|
|
662
|
+
'utility' => $util,
|
|
663
|
+
'config' => $config,
|
|
664
|
+
'options' => ['clean' => ['values' => self::CANARY['value']]],
|
|
665
|
+
], null));
|
|
666
|
+
$cfgctx = new ${Name}Context(['options' => $built], null);
|
|
667
|
+
$seeded = ($util->clean)($cfgctx, 'config ' . self::CANARY['config'] . ' caller ' . self::CANARY['value']);
|
|
668
|
+
$sinks[] = ['name' => 'config-clean', 'text' => (string)$seeded];
|
|
669
|
+
$this->assertSame('config ' . self::MASK . ' caller ' . self::MASK, $seeded);
|
|
670
|
+
$this->assertSame(['my_zzsens' => self::MASK, 'other' => 'y'],
|
|
671
|
+
($util->clean)($cfgctx, ['my_zzsens' => 'x', 'other' => 'y']));
|
|
672
|
+
$this->assertSame(['keys' => 'zzsens', 'values' => self::CANARY['config']],
|
|
673
|
+
$config['options']['clean']);
|
|
674
|
+
|
|
675
|
+
// With no clean option at all, the schema defaults still apply.
|
|
676
|
+
$respond404 = self::scenarios()['notfound'];
|
|
677
|
+
$bwatcher = self::capture_feature($sinks);
|
|
678
|
+
$bare = new ${Name}SDK([
|
|
679
|
+
'apikey' => self::CANARY['apikey'],
|
|
680
|
+
'secret' => self::CANARY['secret'],
|
|
681
|
+
'headers' => ['X-Custom-Token' => self::CANARY['header']],
|
|
682
|
+
'extend' => [$bwatcher],
|
|
683
|
+
'utility' => [
|
|
684
|
+
'fetcher' => function (${Name}Context $_ctx, string $url, array $fetchdef) use ($respond404): array {
|
|
685
|
+
return $respond404($url, $fetchdef);
|
|
686
|
+
},
|
|
687
|
+
],
|
|
688
|
+
]);
|
|
689
|
+
[$bareerr, $_bexplain] = self::drive($bare, $bwatcher, $target, ['explain' => ['on' => true]], $sinks);
|
|
690
|
+
$this->assertNotNull($bareerr, 'the 404 should fail');
|
|
691
|
+
|
|
692
|
+
// A feature's name is not a field name: only the sensitive names
|
|
693
|
+
// inside its settings register. An entity block, of per-entity
|
|
694
|
+
// settings or seeded records keyed by entity name and id, is not read.
|
|
695
|
+
$featured = new ${Name}SDK([
|
|
696
|
+
'apikey' => self::CANARY['apikey'],
|
|
697
|
+
'feature' => [
|
|
698
|
+
'zzsecrets' => ['active' => false, 'kind' => 'PLAINSETTING-q8w2e4r6'],
|
|
699
|
+
'zzfeat' => ['active' => false, 'apitoken' => 'FEATTOKEN-z9y8x7w6'],
|
|
700
|
+
'test' => ['active' => false, 'entity' => [
|
|
701
|
+
'zztoken' => ['ZZTOKEN01' => ['note' => 'PLAINRECORD-t5r3e1w9']]]],
|
|
702
|
+
],
|
|
703
|
+
'entity' => ['zztoken' => ['alias' => ['zzkey' => 'PLAINALIAS-m2n4b6v8']]],
|
|
704
|
+
]);
|
|
705
|
+
$fclean = $featured->get_utility()->clean;
|
|
706
|
+
$fplain = $fclean($featured->get_root_ctx(), 'kind PLAINSETTING-q8w2e4r6');
|
|
707
|
+
$ftoken = $fclean($featured->get_root_ctx(), 'token FEATTOKEN-z9y8x7w6');
|
|
708
|
+
$frecord = $fclean($featured->get_root_ctx(), 'record PLAINRECORD-t5r3e1w9');
|
|
709
|
+
$falias = $fclean($featured->get_root_ctx(), 'alias PLAINALIAS-m2n4b6v8');
|
|
710
|
+
|
|
711
|
+
$leaked = [];
|
|
712
|
+
$excerpt = '';
|
|
713
|
+
foreach ($sinks as $s) {
|
|
714
|
+
$found = self::leaks($s['text']);
|
|
715
|
+
if (0 < count($found)) {
|
|
716
|
+
$leaked[] = $s['name'] . ' [' . implode(', ', $found) . ']';
|
|
717
|
+
if ('' === $excerpt) {
|
|
718
|
+
// Where the first leak sits, so a failure names its path.
|
|
719
|
+
$at = strpos($s['text'], $found[0]);
|
|
720
|
+
$excerpt = "\\n" . $s['name'] . ' near the leak: ' .
|
|
721
|
+
substr($s['text'], max(0, $at - 600), 700);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// The one line the generator's compile lane reads.
|
|
727
|
+
fwrite(STDERR, sprintf("clean: swept %d surface(s), %d leak(s)\\n",
|
|
728
|
+
count($sinks), count($leaked)));
|
|
729
|
+
|
|
730
|
+
$this->assertSame([], $leaked, 'credential leaked through: ' . implode('; ', $leaked) . $excerpt);
|
|
731
|
+
|
|
732
|
+
// The positive half: the slot the credential travelled in is masked,
|
|
733
|
+
// and an unregistered token in a response header is masked by name.
|
|
734
|
+
$notfound = $errors['notfound/throw'] ?? null;
|
|
735
|
+
$this->assertInstanceOf(${Name}Error::class, $notfound, 'the 404 scenario must throw');
|
|
736
|
+
$this->assertSame(404, $notfound->status);
|
|
737
|
+
$spec = $notfound->spec;
|
|
738
|
+
if (!self::AUTH['suppressed']) {
|
|
739
|
+
if ('query' === self::AUTH['where']) {
|
|
740
|
+
$this->assertSame(self::MASK,
|
|
741
|
+
self::header(self::header($spec, 'query'), self::AUTH['name']));
|
|
742
|
+
} elseif ('cookie' === self::AUTH['where']) {
|
|
743
|
+
$this->assertStringContainsString(self::MASK,
|
|
744
|
+
(string)self::header(self::header($spec, 'headers'), 'cookie'));
|
|
745
|
+
} else {
|
|
746
|
+
$this->assertStringEndsWith(self::MASK,
|
|
747
|
+
(string)self::header(self::header($spec, 'headers'), self::AUTH['name']));
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
$this->assertSame(self::MASK, self::header(self::header($spec, 'headers'), 'x-custom-token'));
|
|
751
|
+
|
|
752
|
+
$coded = $errors['coded/throw'] ?? null;
|
|
753
|
+
$this->assertInstanceOf(${Name}Error::class, $coded, 'the coded scenario must throw');
|
|
754
|
+
$this->assertSame('denied_' . self::MASK, $coded->sdk_code);
|
|
755
|
+
$this->assertSame('denied_' . self::MASK, $codederr->getCode());
|
|
756
|
+
|
|
757
|
+
$this->assertSame('kind PLAINSETTING-q8w2e4r6', $fplain);
|
|
758
|
+
$this->assertSame('token ' . self::MASK, $ftoken);
|
|
759
|
+
$this->assertSame('record PLAINRECORD-t5r3e1w9', $frecord);
|
|
760
|
+
$this->assertSame('alias PLAINALIAS-m2n4b6v8', $falias);
|
|
761
|
+
|
|
762
|
+
$explained = $explains['ok/explain'] ?? [];
|
|
763
|
+
$this->assertNotNull($explained['result'] ?? null, 'the explain record should carry the result');
|
|
764
|
+
$this->assertSame(self::MASK,
|
|
765
|
+
self::header(self::header($explained['result'], 'headers'), 'x-session-token'));
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
public function test_the_sweep_can_see_a_leak_clean_switched_off_shows_the_credential(): void
|
|
769
|
+
{
|
|
770
|
+
$target = self::usable_op();
|
|
771
|
+
if (null === $target) {
|
|
772
|
+
$this->markTestSkipped('no operation of this SDK completes against a plain 200; nothing to sweep');
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
$sinks = new \\ArrayObject();
|
|
776
|
+
[$sdk, $watcher] = self::make_sdk(self::scenarios()['notfound'], $sinks, ['active' => false]);
|
|
777
|
+
[$err, $_explain] = self::drive($sdk, $watcher, $target, [], $sinks);
|
|
778
|
+
$this->assertNotNull($err);
|
|
779
|
+
|
|
780
|
+
// Explaining a failure must not cost it its error.
|
|
781
|
+
$discard = new \\ArrayObject();
|
|
782
|
+
[$esdk, $ewatcher] = self::make_sdk(self::scenarios()['notfound'], $discard, ['active' => false]);
|
|
783
|
+
[$explained, $_eexplain] = self::drive($esdk, $ewatcher, $target, ['explain' => ['on' => true]], $discard);
|
|
784
|
+
$this->assertSame($err->getMessage(), $explained?->getMessage(), 'with clean off, explain lost the error');
|
|
785
|
+
|
|
786
|
+
$leaked = 0;
|
|
787
|
+
foreach ($sinks as $s) {
|
|
788
|
+
if (0 < count(self::leaks($s['text']))) {
|
|
789
|
+
$leaked++;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
$this->assertGreaterThan(0, $leaked, 'with clean off, nothing showed the canary: the sweep is blind');
|
|
793
|
+
|
|
794
|
+
if (!self::AUTH['suppressed']) {
|
|
795
|
+
$text = (string)json_encode($err instanceof ${Name}Error ? $err->spec : null);
|
|
796
|
+
$this->assertTrue(
|
|
797
|
+
str_contains($text, self::CANARY['apikey'])
|
|
798
|
+
|| str_contains($text, base64_encode(self::CANARY['apikey'] . ':' . self::CANARY['secret'])),
|
|
799
|
+
'the raw spec should carry the credential when clean is off');
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
`
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
export {
|
|
808
|
+
TestClean
|
|
809
|
+
}
|