@voxgig/sdkgen 4.5.3 → 4.7.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/check.js +15 -0
- package/dist/action/check.js.map +1 -1
- package/dist/cmp/AgentGuide.js +1 -1
- package/dist/cmp/AgentGuide.js.map +1 -1
- package/dist/cmp/AgentGuideContent.d.ts +1 -1
- package/dist/cmp/AgentGuideContent.js +8 -2
- package/dist/cmp/AgentGuideContent.js.map +1 -1
- package/dist/cmp/Feature.js +22 -0
- package/dist/cmp/Feature.js.map +1 -1
- package/dist/cmp/FeatureDocs.d.ts +1 -1
- package/dist/cmp/FeatureDocs.js +6 -1
- package/dist/cmp/FeatureDocs.js.map +1 -1
- package/dist/cmp/ReadmeExplanation.js +3 -1
- package/dist/cmp/ReadmeExplanation.js.map +1 -1
- package/dist/cmp/ReadmeFeatures.js +1 -1
- package/dist/cmp/ReadmeFeatures.js.map +1 -1
- package/dist/cmp/ReadmeRefFeatures.js +1 -1
- package/dist/cmp/ReadmeRefFeatures.js.map +1 -1
- package/dist/cmp/TestControl.d.ts +4 -0
- package/dist/cmp/TestControl.js +51 -0
- package/dist/cmp/TestControl.js.map +1 -0
- package/dist/helpers/applicability.d.ts +6 -0
- package/dist/helpers/applicability.js +103 -0
- package/dist/helpers/applicability.js.map +1 -0
- package/dist/helpers/collectDeps.js +4 -2
- package/dist/helpers/collectDeps.js.map +1 -1
- package/dist/helpers/featureSource.d.ts +3 -1
- package/dist/helpers/featureSource.js +105 -0
- package/dist/helpers/featureSource.js.map +1 -1
- package/dist/helpers/serverVars.d.ts +2 -1
- package/dist/helpers/serverVars.js +17 -0
- package/dist/helpers/serverVars.js.map +1 -1
- package/dist/sdkgen.d.ts +5 -3
- package/dist/sdkgen.js +16 -4
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utility.d.ts +2 -1
- package/dist/utility.js +59 -1
- package/dist/utility.js.map +1 -1
- package/model/sdkgen.aon +97 -0
- package/package.json +1 -1
- package/project/.sdk/model/feature/feature-index.aon +1 -0
- package/project/.sdk/model/feature/secrets.aon +200 -0
- package/project/.sdk/model/target/ts.aon +8 -1
- package/project/.sdk/src/cmp/c/Config_c.ts +5 -1
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +5 -1
- package/project/.sdk/src/cmp/cpp/Main_cpp.ts +2 -1
- package/project/.sdk/src/cmp/cpp/Test_cpp.ts +5 -1
- package/project/.sdk/src/cmp/csharp/Config_csharp.ts +5 -1
- package/project/.sdk/src/cmp/csharp/Main_csharp.ts +2 -1
- package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +30 -6
- package/project/.sdk/src/cmp/csharp/TestEntity_csharp.ts +29 -5
- package/project/.sdk/src/cmp/csharp/Test_csharp.ts +5 -1
- package/project/.sdk/src/cmp/dart/Config_dart.ts +5 -1
- package/project/.sdk/src/cmp/dart/Main_dart.ts +2 -1
- package/project/.sdk/src/cmp/dart/TestDirect_dart.ts +23 -6
- package/project/.sdk/src/cmp/dart/TestEntity_dart.ts +28 -7
- package/project/.sdk/src/cmp/dart/Test_dart.ts +5 -1
- package/project/.sdk/src/cmp/elixir/Config_elixir.ts +5 -1
- package/project/.sdk/src/cmp/elixir/Main_elixir.ts +5 -1
- package/project/.sdk/src/cmp/go/Config_go.ts +5 -1
- package/project/.sdk/src/cmp/go/Main_go.ts +8 -3
- package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +16 -3
- package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +16 -3
- package/project/.sdk/src/cmp/go/TestDirect_go.ts +28 -4
- package/project/.sdk/src/cmp/go/TestEntity_go.ts +43 -10
- package/project/.sdk/src/cmp/go/Test_go.ts +5 -1
- package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +5 -5
- package/project/.sdk/src/cmp/java/Config_java.ts +22 -1
- package/project/.sdk/src/cmp/java/Main_java.ts +7 -2
- package/project/.sdk/src/cmp/java/ReadmeQuick_java.ts +17 -3
- package/project/.sdk/src/cmp/java/ReadmeTopQuick_java.ts +17 -3
- package/project/.sdk/src/cmp/java/TestDirect_java.ts +23 -4
- package/project/.sdk/src/cmp/java/TestEntity_java.ts +29 -4
- package/project/.sdk/src/cmp/java/Test_java.ts +5 -1
- package/project/.sdk/src/cmp/js/Config_js.ts +5 -1
- package/project/.sdk/src/cmp/js/Main_js.ts +7 -2
- package/project/.sdk/src/cmp/js/Package_js.ts +4 -1
- package/project/.sdk/src/cmp/js/TestDirect_js.ts +30 -5
- package/project/.sdk/src/cmp/js/TestEntity_js.ts +35 -5
- package/project/.sdk/src/cmp/js/Test_js.ts +5 -1
- package/project/.sdk/src/cmp/js/fragment/Direct.test.fragment.js +7 -1
- package/project/.sdk/src/cmp/js/fragment/Entity.test.fragment.js +7 -1
- package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +5 -1
- package/project/.sdk/src/cmp/kotlin/Main_kotlin.ts +2 -1
- package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +5 -1
- package/project/.sdk/src/cmp/lean/Config_lean.ts +5 -1
- package/project/.sdk/src/cmp/lua/Config_lua.ts +5 -1
- package/project/.sdk/src/cmp/lua/Main_lua.ts +7 -2
- package/project/.sdk/src/cmp/lua/Package_lua.ts +4 -1
- package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +28 -4
- package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +24 -4
- package/project/.sdk/src/cmp/lua/Test_lua.ts +5 -1
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +5 -1
- package/project/.sdk/src/cmp/ocaml/Gitignore_ocaml.ts +1 -0
- package/project/.sdk/src/cmp/perl/Main_perl.ts +7 -2
- package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +25 -4
- package/project/.sdk/src/cmp/perl/TestEntity_perl.ts +24 -4
- package/project/.sdk/src/cmp/perl/Test_perl.ts +5 -1
- package/project/.sdk/src/cmp/perl/utility_perl.ts +10 -0
- package/project/.sdk/src/cmp/php/Config_php.ts +5 -1
- package/project/.sdk/src/cmp/php/Main_php.ts +7 -2
- package/project/.sdk/src/cmp/php/TestDirect_php.ts +24 -5
- package/project/.sdk/src/cmp/php/TestEntity_php.ts +28 -4
- package/project/.sdk/src/cmp/php/Test_php.ts +5 -1
- package/project/.sdk/src/cmp/py/Config_py.ts +5 -1
- package/project/.sdk/src/cmp/py/Main_py.ts +7 -2
- package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +16 -3
- package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +16 -3
- package/project/.sdk/src/cmp/py/TestDirect_py.ts +24 -5
- package/project/.sdk/src/cmp/py/TestEntity_py.ts +23 -3
- package/project/.sdk/src/cmp/py/Test_py.ts +5 -1
- package/project/.sdk/src/cmp/rb/Config_rb.ts +5 -1
- package/project/.sdk/src/cmp/rb/Main_rb.ts +7 -2
- package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +24 -5
- package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +24 -4
- package/project/.sdk/src/cmp/rb/Test_rb.ts +5 -1
- package/project/.sdk/src/cmp/rb/utility_rb.ts +3 -1
- package/project/.sdk/src/cmp/rust/Config_rust.ts +5 -1
- package/project/.sdk/src/cmp/rust/Main_rust.ts +7 -2
- package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +31 -4
- package/project/.sdk/src/cmp/rust/TestEntity_rust.ts +37 -4
- package/project/.sdk/src/cmp/rust/Test_rust.ts +5 -1
- package/project/.sdk/src/cmp/scala/Config_scala.ts +5 -1
- package/project/.sdk/src/cmp/scala/Main_scala.ts +7 -2
- package/project/.sdk/src/cmp/scala/Test_scala.ts +5 -1
- package/project/.sdk/src/cmp/swift/Config_swift.ts +5 -1
- package/project/.sdk/src/cmp/ts/Config_ts.ts +55 -9
- package/project/.sdk/src/cmp/ts/Main_ts.ts +67 -3
- package/project/.sdk/src/cmp/ts/Package_ts.ts +4 -1
- package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +15 -3
- package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +18 -5
- package/project/.sdk/src/cmp/ts/ReadmeTopQuick_ts.ts +19 -5
- package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +75 -8
- package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +36 -5
- package/project/.sdk/src/cmp/ts/Test_ts.ts +5 -1
- package/project/.sdk/src/cmp/ts/fragment/Direct.test.fragment.ts +9 -2
- package/project/.sdk/src/cmp/ts/fragment/Entity.test.fragment.ts +9 -2
- package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +7 -0
- package/project/.sdk/src/cmp/zig/Config_zig.ts +5 -1
- package/project/.sdk/tm/c/utility/make_options.c +22 -0
- package/project/.sdk/tm/clojure/src/sdk/core.clj +18 -1
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +23 -0
- package/project/.sdk/tm/csharp/test/CustomUtilityTest.cs +15 -0
- package/project/.sdk/tm/csharp/test/Runner.cs +45 -0
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +23 -0
- package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +22 -0
- package/project/.sdk/tm/dart/test/utility.dart +39 -0
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +19 -0
- package/project/.sdk/tm/go/core/context.go +1 -1
- package/project/.sdk/tm/go/feature/cost_feature.go +1 -1
- package/project/.sdk/tm/go/feature/paging_feature.go +5 -5
- package/project/.sdk/tm/go/feature/test_feature.go +12 -5
- package/project/.sdk/tm/go/test/custom_utility_test.go +15 -0
- package/project/.sdk/tm/go/test/feature_corpus_test.go +7 -0
- package/project/.sdk/tm/go/test/runner_test.go +46 -0
- package/project/.sdk/tm/go/test/struct_runner_test.go +7 -7
- package/project/.sdk/tm/go/test/struct_utility_test.go +63 -27
- package/project/.sdk/tm/go/utility/fetcher.go +2 -2
- package/project/.sdk/tm/go/utility/make_options.go +24 -5
- package/project/.sdk/tm/go/utility/make_point.go +1 -1
- package/project/.sdk/tm/go/utility/make_spec.go +17 -1
- package/project/.sdk/tm/go/utility/prepare_auth.go +1 -1
- package/project/.sdk/tm/go/utility/struct/voxgigstruct.go +370 -307
- package/project/.sdk/tm/go/utility/transform_request.go +29 -1
- package/project/.sdk/tm/go/utility/transform_response.go +20 -1
- package/project/.sdk/tm/java/test/CustomUtilityTest.java +10 -0
- package/project/.sdk/tm/java/test/FeatureCorpusTest.java +7 -0
- package/project/.sdk/tm/java/test/PrimaryUtilityTest.java +6 -0
- package/project/.sdk/tm/java/test/RunnerSupport.java +43 -0
- package/project/.sdk/tm/java/utility/MakeOptions.java +84 -1
- package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +24 -0
- package/project/.sdk/tm/js/src/utility/StructUtility.js +2076 -1837
- package/project/.sdk/tm/js/test/feature/Corpus.test.js +9 -0
- package/project/.sdk/tm/js/test/sdk-test-control.json +19 -0
- package/project/.sdk/tm/js/test/utility.js +91 -1
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +21 -0
- package/project/.sdk/tm/lean/src/SdkUtility.lean +12 -1
- package/project/.sdk/tm/lua/test/runner.lua +40 -0
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +11 -0
- package/project/.sdk/tm/ocaml/test/harness.ml +14 -4
- package/project/.sdk/tm/perl/t/runner.pm +30 -0
- package/project/.sdk/tm/perl/utility/make_options.pm +18 -0
- package/project/.sdk/tm/php/test/FeatureCorpusTest.php +10 -1
- package/project/.sdk/tm/php/test/Runner.php +36 -0
- package/project/.sdk/tm/php/utility/MakeOptions.php +24 -0
- package/project/.sdk/tm/py/pkg/utility/make_options.py +20 -0
- package/project/.sdk/tm/py/test/runner.py +32 -0
- package/project/.sdk/tm/py/test/test_feature_corpus.py +10 -1
- package/project/.sdk/tm/rb/test/feature_corpus_test.rb +9 -1
- package/project/.sdk/tm/rb/test/runner.rb +25 -0
- package/project/.sdk/tm/rb/utility/make_options.rb +17 -0
- package/project/.sdk/tm/rust/tests/common/mod.rs +32 -0
- package/project/.sdk/tm/rust/utility/make_options.rs +26 -1
- package/project/.sdk/tm/scala/utility/Make.scala +17 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +21 -0
- package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +419 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +387 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sigv4.ts +175 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +51 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/Registry.ts +90 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/addr.ts +48 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/aws.ts +219 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/azuresecrets.ts +144 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/boru.ts +113 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/doppler.ts +77 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/dotenv.ts +51 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/env.ts +31 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/file.ts +54 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/gcpsecrets.ts +118 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/hashicorp.ts +159 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/http.ts +66 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/infisical.ts +107 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/memory.ts +33 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/onepassword.ts +120 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/support.ts +176 -0
- package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +18 -0
- package/project/.sdk/tm/ts/src/utility/StructUtility.ts +1321 -710
- package/project/.sdk/tm/ts/test/feature/Corpus.test.ts +9 -0
- package/project/.sdk/tm/ts/test/feature/secrets/Secrets.test.ts +764 -0
- package/project/.sdk/tm/ts/test/omni.test.ts +84 -0
- package/project/.sdk/tm/ts/test/omni.ts +86 -0
- package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +6 -2
- package/project/.sdk/tm/ts/test/utility/StructUtility.test.ts +2 -2
- package/project/.sdk/tm/ts/test/utility.ts +122 -0
- package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +694 -0
- package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +244 -0
- package/project/.sdk/tm/ts/test/vendor/omni/compat.ts +188 -0
- package/project/.sdk/tm/ts/test/vendor/omni/index.ts +38 -0
- package/project/.sdk/tm/zig/core/utility.zig +24 -1
- package/project/sdkgen-package.json +2 -1
- package/src/action/check.ts +18 -0
- package/src/cmp/AgentGuide.ts +1 -1
- package/src/cmp/AgentGuideContent.ts +9 -2
- package/src/cmp/Feature.ts +24 -0
- package/src/cmp/FeatureDocs.ts +7 -1
- package/src/cmp/ReadmeExplanation.ts +4 -1
- package/src/cmp/ReadmeFeatures.ts +1 -1
- package/src/cmp/ReadmeRefFeatures.ts +1 -1
- package/src/cmp/TestControl.ts +64 -0
- package/src/helpers/applicability.ts +123 -0
- package/src/helpers/collectDeps.ts +5 -1
- package/src/helpers/featureSource.ts +130 -0
- package/src/helpers/serverVars.ts +19 -0
- package/src/sdkgen.ts +16 -1
- package/src/utility.ts +71 -1
- package/project/.sdk/tm/ts/test/runner.ts +0 -425
|
@@ -17,9 +17,12 @@ import {
|
|
|
17
17
|
cmp,
|
|
18
18
|
snakify,
|
|
19
19
|
isAuthActive,
|
|
20
|
+
serverVarEnv,
|
|
21
|
+
serverVariables,
|
|
20
22
|
isHttpBasicAuth,
|
|
21
23
|
jsProp,
|
|
22
|
-
jsOptProp, envName, envToken, liveStrict
|
|
24
|
+
jsOptProp, envName, envToken, liveStrict,
|
|
25
|
+
jsKey
|
|
23
26
|
} from '@voxgig/sdkgen'
|
|
24
27
|
|
|
25
28
|
|
|
@@ -47,7 +50,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
|
|
|
47
50
|
const authActive = isAuthActive(model)
|
|
48
51
|
const authBasic = authActive && isHttpBasicAuth(model)
|
|
49
52
|
const apikeyEnvEntry = authActive
|
|
50
|
-
? `\n '${PROJECTNAME}_APIKEY': '
|
|
53
|
+
? `\n '${PROJECTNAME}_APIKEY': '',${authBasic ? `\n '${PROJECTNAME}_SECRET': '',` : ''}`
|
|
51
54
|
: ''
|
|
52
55
|
const apikeyLiveField = authActive
|
|
53
56
|
? `
|
|
@@ -55,6 +58,25 @@ const TestDirect = cmp(function TestDirect(props: any) {
|
|
|
55
58
|
secret: env.${PROJECTNAME}_SECRET,` : ''}`
|
|
56
59
|
: ''
|
|
57
60
|
|
|
61
|
+
// A templated server URL (OpenAPI server variables) makes a LIVE client
|
|
62
|
+
// impossible to construct without values: makeOptions raises rather than
|
|
63
|
+
// request a URL with a literal `{account_id}` in it. Taken from the
|
|
64
|
+
// environment, the same way the apikey is.
|
|
65
|
+
//
|
|
66
|
+
// Keys are quoted and the env read is bracketed via jsKey/jsProp: a server
|
|
67
|
+
// variable name is spec-derived and need not be a JS identifier — the URL
|
|
68
|
+
// grammar admits a leading digit ({2fa}), and a declared-but-unreferenced
|
|
69
|
+
// variable ({edge-zone}) is not constrained at all. Bare `name:` and
|
|
70
|
+
// `env.PROJ_SERVER_EDGE-ZONE` are both syntax errors.
|
|
71
|
+
const svars = serverVariables(model)
|
|
72
|
+
const serverEnvEntry = svars
|
|
73
|
+
.map((v: any) => `\n '${serverVarEnv(PROJECTNAME, v.name)}': ${JSON.stringify(v.dflt)},`).join('')
|
|
74
|
+
const serverLiveField = 0 === svars.length ? '' : `
|
|
75
|
+
server: {${svars
|
|
76
|
+
.map((v: any) => `
|
|
77
|
+
${jsKey(v.name)}: ${jsProp('env', serverVarEnv(PROJECTNAME, v.name))},`).join('')}
|
|
78
|
+
},`
|
|
79
|
+
|
|
58
80
|
const opnames = Object.keys(entity.op || {})
|
|
59
81
|
const hasLoad = opnames.includes('load')
|
|
60
82
|
const hasList = opnames.includes('list')
|
|
@@ -86,14 +108,17 @@ function directSetup(mockres?: any) {
|
|
|
86
108
|
|
|
87
109
|
const env = envOverride({
|
|
88
110
|
'${entidEnvVar}': {},
|
|
89
|
-
'${PROJECTNAME}_TEST_LIVE': 'FALSE',${apikeyEnvEntry}
|
|
111
|
+
'${PROJECTNAME}_TEST_LIVE': 'FALSE',${apikeyEnvEntry}${serverEnvEntry}
|
|
90
112
|
})
|
|
91
113
|
|
|
92
114
|
const live = 'TRUE' === env.${PROJECTNAME}_TEST_LIVE
|
|
93
115
|
|
|
94
116
|
if (live) {
|
|
95
|
-
|
|
96
|
-
|
|
117
|
+
// Merged so the generated fields win: sdk-test-control.json's
|
|
118
|
+
// test.client.options adds to the live client, it does not redirect it.
|
|
119
|
+
const client = new ${nom(model.const, 'Name')}SDK(
|
|
120
|
+
Object.assign({}, liveClientOptions(), {${apikeyLiveField}${serverLiveField}
|
|
121
|
+
}))
|
|
97
122
|
|
|
98
123
|
let idmap: any = env['${entidEnvVar}']
|
|
99
124
|
if ('string' === typeof idmap && idmap.startsWith('{')) {
|
|
@@ -210,7 +235,21 @@ function generateDirectGraphql(
|
|
|
210
235
|
${varAsserts}`
|
|
211
236
|
|
|
212
237
|
const checks = strict ?
|
|
213
|
-
|
|
238
|
+
` if (setup.live) {
|
|
239
|
+
// STRICT live mode: a non-2xx is a real failure - this project owns
|
|
240
|
+
// the server it points at, so there is nothing to be lenient about.
|
|
241
|
+
//
|
|
242
|
+
// What is NOT asserted here is the MOCK's own fixtures. \`direct01\`
|
|
243
|
+
// is a scripted id and \`calls\` records the mock transport; neither
|
|
244
|
+
// exists on a live run, so asserting them made strict mode mean
|
|
245
|
+
// "compare the live server against the mock's script" - a suite that
|
|
246
|
+
// could not pass against any real API, including this project's own.
|
|
247
|
+
assert(result.ok === true,
|
|
248
|
+
'live request failed: ' + result.status + ' ' + JSON.stringify(result.data))
|
|
249
|
+
assert(result.status >= 200 && result.status < 300)
|
|
250
|
+
assert(null != result.data)
|
|
251
|
+
} else {
|
|
252
|
+
${offlineChecks} }` :
|
|
214
253
|
` if (setup.live) {
|
|
215
254
|
// Live mode is lenient: synthetic ids frequently fail server-side
|
|
216
255
|
// validation. Skip rather than fail when the call doesn't come back
|
|
@@ -429,7 +468,21 @@ ${loadParams.map((p: any, i: number) => ` ${jsProp('params', p.name)} = 'di
|
|
|
429
468
|
${paramAsserts}`
|
|
430
469
|
|
|
431
470
|
const loadChecks = strict ?
|
|
432
|
-
|
|
471
|
+
` if (setup.live) {
|
|
472
|
+
// STRICT live mode: a non-2xx is a real failure - this project owns
|
|
473
|
+
// the server it points at, so there is nothing to be lenient about.
|
|
474
|
+
//
|
|
475
|
+
// What is NOT asserted here is the MOCK's own fixtures. \`direct01\`
|
|
476
|
+
// is a scripted id and \`calls\` records the mock transport; neither
|
|
477
|
+
// exists on a live run, so asserting them made strict mode mean
|
|
478
|
+
// "compare the live server against the mock's script" - a suite that
|
|
479
|
+
// could not pass against any real API, including this project's own.
|
|
480
|
+
assert(result.ok === true,
|
|
481
|
+
'live request failed: ' + result.status + ' ' + JSON.stringify(result.data))
|
|
482
|
+
assert(result.status >= 200 && result.status < 300)
|
|
483
|
+
assert(null != result.data)
|
|
484
|
+
} else {
|
|
485
|
+
${offlineChecks} }` :
|
|
433
486
|
` if (setup.live) {
|
|
434
487
|
// Live mode is lenient: synthetic IDs frequently 4xx. Skip rather
|
|
435
488
|
// than fail when the load endpoint isn't reachable with the IDs we
|
|
@@ -543,7 +596,21 @@ ${mockLines}
|
|
|
543
596
|
${paramAsserts}`
|
|
544
597
|
|
|
545
598
|
const listChecks = strict ?
|
|
546
|
-
|
|
599
|
+
` if (setup.live) {
|
|
600
|
+
// STRICT live mode: a non-2xx is a real failure - this project owns
|
|
601
|
+
// the server it points at, so there is nothing to be lenient about.
|
|
602
|
+
//
|
|
603
|
+
// What is NOT asserted here is the MOCK's own fixtures. \`direct01\`
|
|
604
|
+
// is a scripted id and \`calls\` records the mock transport; neither
|
|
605
|
+
// exists on a live run, so asserting them made strict mode mean
|
|
606
|
+
// "compare the live server against the mock's script" - a suite that
|
|
607
|
+
// could not pass against any real API, including this project's own.
|
|
608
|
+
assert(result.ok === true,
|
|
609
|
+
'live request failed: ' + result.status + ' ' + JSON.stringify(result.data))
|
|
610
|
+
assert(result.status >= 200 && result.status < 300)
|
|
611
|
+
assert(null != result.data)
|
|
612
|
+
} else {
|
|
613
|
+
${offlineChecks} }` :
|
|
547
614
|
` if (setup.live) {
|
|
548
615
|
// Live mode is lenient: synthetic IDs frequently 4xx and the list-
|
|
549
616
|
// response shape varies wildly across public APIs. Skip rather than
|
|
@@ -30,8 +30,12 @@ import {
|
|
|
30
30
|
cmp,
|
|
31
31
|
each,
|
|
32
32
|
isAuthActive,
|
|
33
|
+
serverVarEnv,
|
|
34
|
+
serverVariables,
|
|
33
35
|
isHttpBasicAuth,
|
|
34
|
-
entityDataIdField, envName, envToken
|
|
36
|
+
entityDataIdField, envName, envToken,
|
|
37
|
+
jsKey,
|
|
38
|
+
jsProp
|
|
35
39
|
} from '@voxgig/sdkgen'
|
|
36
40
|
|
|
37
41
|
|
|
@@ -67,7 +71,7 @@ const TestEntity = cmp(function TestEntity(props: any) {
|
|
|
67
71
|
const authActive = isAuthActive(model)
|
|
68
72
|
const authBasic = authActive && isHttpBasicAuth(model)
|
|
69
73
|
const apikeyEnvEntry = authActive
|
|
70
|
-
? `\n '${PROJENVNAME}_APIKEY': '
|
|
74
|
+
? `\n '${PROJENVNAME}_APIKEY': '',${authBasic ? `\n '${PROJENVNAME}_SECRET': '',` : ''}`
|
|
71
75
|
: ''
|
|
72
76
|
const apikeyLiveField = authActive
|
|
73
77
|
? `
|
|
@@ -75,6 +79,25 @@ const TestEntity = cmp(function TestEntity(props: any) {
|
|
|
75
79
|
secret: env.${PROJENVNAME}_SECRET,` : ''}`
|
|
76
80
|
: ''
|
|
77
81
|
|
|
82
|
+
// A templated server URL (OpenAPI server variables) makes a LIVE client
|
|
83
|
+
// impossible to construct without values: makeOptions raises rather than
|
|
84
|
+
// request a URL with a literal `{account_id}` in it. So the live suite
|
|
85
|
+
// takes them from the environment the same way it takes the apikey.
|
|
86
|
+
//
|
|
87
|
+
// Keys are quoted and the env read is bracketed via jsKey/jsProp: a server
|
|
88
|
+
// variable name is spec-derived and need not be a JS identifier — the URL
|
|
89
|
+
// grammar admits a leading digit ({2fa}), and a declared-but-unreferenced
|
|
90
|
+
// variable ({edge-zone}) is not constrained at all. Bare `name:` and
|
|
91
|
+
// `env.PROJ_SERVER_EDGE-ZONE` are both syntax errors.
|
|
92
|
+
const svars = serverVariables(model)
|
|
93
|
+
const serverEnvEntry = svars
|
|
94
|
+
.map((v: any) => `\n '${serverVarEnv(PROJENVNAME, v.name)}': ${JSON.stringify(v.dflt)},`).join('')
|
|
95
|
+
const serverLiveField = 0 === svars.length ? '' : `
|
|
96
|
+
server: {${svars
|
|
97
|
+
.map((v: any) => `
|
|
98
|
+
${jsKey(v.name)}: ${jsProp('env', serverVarEnv(PROJENVNAME, v.name))},`).join('')}
|
|
99
|
+
},`
|
|
100
|
+
|
|
78
101
|
// TODO: should be a utility function
|
|
79
102
|
const ff = projectPath('src/cmp/ts/fragment/')
|
|
80
103
|
|
|
@@ -156,7 +179,7 @@ function basicSetup(extra?: any) {
|
|
|
156
179
|
const env = envOverride({
|
|
157
180
|
'${PROJENVNAME}_TEST_${ENTENVNAME}_ENTID': idmap,
|
|
158
181
|
'${PROJENVNAME}_TEST_LIVE': 'FALSE',
|
|
159
|
-
'${PROJENVNAME}_TEST_EXPLAIN': 'FALSE',${apikeyEnvEntry}
|
|
182
|
+
'${PROJENVNAME}_TEST_EXPLAIN': 'FALSE',${apikeyEnvEntry}${serverEnvEntry}
|
|
160
183
|
})
|
|
161
184
|
|
|
162
185
|
idmap = env['${PROJENVNAME}_TEST_${ENTENVNAME}_ENTID']
|
|
@@ -165,9 +188,17 @@ function basicSetup(extra?: any) {
|
|
|
165
188
|
|
|
166
189
|
if (live) {
|
|
167
190
|
client = new ${model.Name}SDK(merge([
|
|
168
|
-
|
|
191
|
+
// FIRST, so the generated fields below win: sdk-test-control.json's
|
|
192
|
+
// test.client.options adds to the live client, it does not redirect it.
|
|
193
|
+
liveClientOptions(),
|
|
194
|
+
{${apikeyLiveField}${serverLiveField}
|
|
169
195
|
},
|
|
170
|
-
extra
|
|
196
|
+
// 'extra || {}', not a bare 'extra': struct.merge returns UNDEFINED when the
|
|
197
|
+
// last entry is undefined, and basicSetup is normally called with no
|
|
198
|
+
// argument at all - so a bare 'extra' silently discarded the apikey
|
|
199
|
+
// and server values above and handed the SDK undefined. Harmless
|
|
200
|
+
// while there was nothing in that object; not harmless now.
|
|
201
|
+
extra || {}
|
|
171
202
|
]))
|
|
172
203
|
}
|
|
173
204
|
|
|
@@ -3,7 +3,8 @@ import type {
|
|
|
3
3
|
ModelEntity
|
|
4
4
|
} from '@voxgig/apidef'
|
|
5
5
|
|
|
6
|
-
import { cmp, each, Folder, entityCollection
|
|
6
|
+
import { cmp, each, Folder, entityCollection,
|
|
7
|
+
TestControl } from '@voxgig/sdkgen'
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
// import { Quick } from './Quick_ts'
|
|
@@ -19,6 +20,9 @@ const Test = cmp(function Test(props: any) {
|
|
|
19
20
|
const { target } = props
|
|
20
21
|
|
|
21
22
|
Folder({ name: 'test' }, () => {
|
|
23
|
+
|
|
24
|
+
// Write-once: a project's edited control file survives regeneration.
|
|
25
|
+
TestControl({ target, dir: 'test' })
|
|
22
26
|
// Quick({ target })
|
|
23
27
|
// TestMain({ target })
|
|
24
28
|
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
const envlocal = __dirname + '/../../../.env.local'
|
|
3
|
-
require('dotenv').config({ quiet: true, path: [envlocal] })
|
|
4
2
|
|
|
5
3
|
import { test, describe, afterEach } from 'node:test'
|
|
6
4
|
import assert from 'node:assert'
|
|
@@ -10,12 +8,21 @@ import { ProjectNameSDK } from '../../..'
|
|
|
10
8
|
|
|
11
9
|
import {
|
|
12
10
|
envOverride,
|
|
11
|
+
liveClientOptions,
|
|
13
12
|
liveDelay,
|
|
13
|
+
loadEnvLocal,
|
|
14
14
|
maybeSkipControl,
|
|
15
15
|
skipIfMissingIds,
|
|
16
16
|
} from '../../utility'
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
// AFTER the imports on purpose: TypeScript hoists `import` above any
|
|
20
|
+
// statement in the emitted CommonJS, so a loader placed above them would
|
|
21
|
+
// run only after every imported module had already been evaluated - and
|
|
22
|
+
// anything reading process.env at module scope would miss these values.
|
|
23
|
+
loadEnvLocal(__dirname + '/../../../.env.local')
|
|
24
|
+
|
|
25
|
+
|
|
19
26
|
describe('EntityNameDirect', async () => {
|
|
20
27
|
|
|
21
28
|
// Per-test live pacing. Delay is read from sdk-test-control.json's
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
const envlocal = __dirname + '/../../../.env.local'
|
|
3
|
-
require('dotenv').config({ quiet: true, path: [envlocal] })
|
|
4
2
|
|
|
5
3
|
import Path from 'node:path'
|
|
6
4
|
import * as Fs from 'node:fs'
|
|
@@ -13,7 +11,9 @@ import { ProjectNameSDK, BaseFeature, stdutil } from '../../..'
|
|
|
13
11
|
|
|
14
12
|
import {
|
|
15
13
|
envOverride,
|
|
14
|
+
liveClientOptions,
|
|
16
15
|
liveDelay,
|
|
16
|
+
loadEnvLocal,
|
|
17
17
|
makeCtrl,
|
|
18
18
|
makeMatch,
|
|
19
19
|
makeReqdata,
|
|
@@ -23,6 +23,13 @@ import {
|
|
|
23
23
|
} from '../../utility'
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
// AFTER the imports on purpose: TypeScript hoists `import` above any
|
|
27
|
+
// statement in the emitted CommonJS, so a loader placed above them would
|
|
28
|
+
// run only after every imported module had already been evaluated - and
|
|
29
|
+
// anything reading process.env at module scope would miss these values.
|
|
30
|
+
loadEnvLocal(__dirname + '/../../../.env.local')
|
|
31
|
+
|
|
32
|
+
|
|
26
33
|
describe('EntityNameEntity', async () => {
|
|
27
34
|
|
|
28
35
|
// Per-test live pacing. Delay is read from sdk-test-control.json's
|
|
@@ -9,6 +9,7 @@ import { Utility } from './utility/Utility'
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
import { BaseFeature } from './feature/base/BaseFeature'
|
|
12
|
+
// #SecretsImport
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
const stdutil = new Utility()
|
|
@@ -20,6 +21,7 @@ class ProjectNameSDK {
|
|
|
20
21
|
_utility = new Utility()
|
|
21
22
|
_features: Feature[]
|
|
22
23
|
_rootctx: Context
|
|
24
|
+
// #SecretsField
|
|
23
25
|
|
|
24
26
|
constructor(options?: any) {
|
|
25
27
|
|
|
@@ -92,6 +94,8 @@ class ProjectNameSDK {
|
|
|
92
94
|
return this._utility.struct.clone(this._utility)
|
|
93
95
|
}
|
|
94
96
|
|
|
97
|
+
// #SecretsAccessor
|
|
98
|
+
|
|
95
99
|
|
|
96
100
|
async prepare(fetchargs?: any) {
|
|
97
101
|
const utility = this._utility
|
|
@@ -138,6 +142,8 @@ class ProjectNameSDK {
|
|
|
138
142
|
}
|
|
139
143
|
}
|
|
140
144
|
|
|
145
|
+
// #SecretsResolve
|
|
146
|
+
|
|
141
147
|
// Apply SDK auth (apikey, auth prefix, etc.)
|
|
142
148
|
const authResult = prepareAuth(ctx)
|
|
143
149
|
if (authResult instanceof Error) {
|
|
@@ -334,6 +340,7 @@ const SDK = ProjectNameSDK
|
|
|
334
340
|
export {
|
|
335
341
|
stdutil,
|
|
336
342
|
config,
|
|
343
|
+
// #SecretsExport
|
|
337
344
|
|
|
338
345
|
BaseFeature,
|
|
339
346
|
ProjectNameEntityBase,
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
each,
|
|
9
9
|
isAuthActive,
|
|
10
10
|
isConfigData,
|
|
11
|
+
targetFeatures,
|
|
11
12
|
} from '@voxgig/sdkgen'
|
|
12
13
|
|
|
13
14
|
|
|
@@ -32,7 +33,10 @@ const Config = cmp(async function Config(props: any) {
|
|
|
32
33
|
|
|
33
34
|
const model: Model = ctx$.model
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
// Gated by the applicability tags, so this target never imports or
|
|
37
|
+
// registers a feature it has no source for. One rule, one place:
|
|
38
|
+
// helpers/applicability.
|
|
39
|
+
const feature = targetFeatures(model, target)
|
|
36
40
|
|
|
37
41
|
// The canonical config OBJECT and its JSON, from the shared helper. Both
|
|
38
42
|
// representations render from the same `def`, so they cannot describe
|
|
@@ -22,6 +22,23 @@ voxgig_value* make_options_util(Context* ctx) {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/* `auth: null` is the documented way to disable auth outright, and
|
|
26
|
+
* prepare_auth honours it before it ever reads the apikey. validate would
|
|
27
|
+
* erase it: this port follows the Group A rule, so a stored null reads as
|
|
28
|
+
* "no value" and the optspec's `auth` default fires instead - transmitting
|
|
29
|
+
* the credential the caller withheld. Put the null back afterwards.
|
|
30
|
+
*
|
|
31
|
+
* Unlike js, no delete-before-validate is needed here: Group A means
|
|
32
|
+
* validate DEFAULTS rather than rejecting, so the key can travel through.
|
|
33
|
+
*
|
|
34
|
+
* Read the map DIRECTLY rather than through getp, which applies that same
|
|
35
|
+
* rule and so cannot tell an absent auth from a suppressed one. */
|
|
36
|
+
bool auth_suppressed = false;
|
|
37
|
+
if (voxgig_is_map(options)) {
|
|
38
|
+
voxgig_value* authval = voxgig_map_get(voxgig_as_map(options), "auth");
|
|
39
|
+
auth_suppressed = (NULL != authval && voxgig_is_null(authval));
|
|
40
|
+
}
|
|
41
|
+
|
|
25
42
|
voxgig_value* opts = voxgig_clone(options);
|
|
26
43
|
|
|
27
44
|
// Feature add-order. options.feature may be an ordered list of
|
|
@@ -93,6 +110,11 @@ voxgig_value* make_options_util(Context* ctx) {
|
|
|
93
110
|
opts = validated;
|
|
94
111
|
}
|
|
95
112
|
|
|
113
|
+
/* Restore the suppression the optspec default would otherwise erase. */
|
|
114
|
+
if (auth_suppressed) {
|
|
115
|
+
setp(opts, "auth", voxgig_new_null());
|
|
116
|
+
}
|
|
117
|
+
|
|
96
118
|
// Restore system.fetch.
|
|
97
119
|
if (!v_is_noval(sys_fetch)) {
|
|
98
120
|
voxgig_value* sys = getp(opts, "system");
|
|
@@ -988,7 +988,22 @@
|
|
|
988
988
|
(if (and member (not= member :custom) (contains? members member))
|
|
989
989
|
(oset! util member v)
|
|
990
990
|
(.put ^java.util.Map (oget util :custom) k v))))))
|
|
991
|
-
(let [
|
|
991
|
+
(let [;; `auth` nil is the documented way to disable auth outright, and
|
|
992
|
+
;; u-prepare-auth honours it before it ever reads the apikey. It
|
|
993
|
+
;; cannot survive validate: a stored null reads as "no value", so the
|
|
994
|
+
;; optspec's `auth` default fires and the suppression silently
|
|
995
|
+
;; becomes "use default auth" - transmitting the credential the
|
|
996
|
+
;; caller withheld. Withhold the key for validate, then put the nil
|
|
997
|
+
;; back. Same fix as ts/js/go makeOptions.
|
|
998
|
+
;;
|
|
999
|
+
;; `.containsKey` rather than a nil check on the value: the latter
|
|
1000
|
+
;; cannot tell an ABSENT auth from a suppressed one, and only the
|
|
1001
|
+
;; second is a suppression.
|
|
1002
|
+
auth-suppressed (and (vs/ismap options)
|
|
1003
|
+
(.containsKey ^java.util.Map options "auth")
|
|
1004
|
+
(nil? (.get ^java.util.Map options "auth")))
|
|
1005
|
+
opts0 (let [c (vs/clone options)] (if (vs/ismap c) c (vs/jm)))
|
|
1006
|
+
_ (when auth-suppressed (.remove ^java.util.Map opts0 "auth"))
|
|
992
1007
|
;; Feature add-order. options.feature may be given as an ordered
|
|
993
1008
|
;; ARRAY of {name active ...opts} entries (array position = add
|
|
994
1009
|
;; order) or a {name {opts}} map. Normalize an array to a map (so
|
|
@@ -1037,6 +1052,8 @@
|
|
|
1037
1052
|
merged (vs/merge (vs/jt (vs/jm) cfgopts opts0))
|
|
1038
1053
|
validated (vs/validate merged optspec)
|
|
1039
1054
|
opts (if (vs/ismap validated) validated (vs/jm))
|
|
1055
|
+
;; Restore the suppression the optspec default would otherwise erase.
|
|
1056
|
+
_ (when auth-suppressed (.put ^java.util.Map opts "auth" nil))
|
|
1040
1057
|
;; Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
|
|
1041
1058
|
;; Every placeholder must resolve to a non-empty value: from
|
|
1042
1059
|
;; options["server"] (user), else the config default. A placeholder
|
|
@@ -1107,8 +1107,26 @@ inline Value makeOptions(CtxPtr ctx) {
|
|
|
1107
1107
|
}
|
|
1108
1108
|
}
|
|
1109
1109
|
|
|
1110
|
+
// `auth: null` is the documented way to disable auth outright, and
|
|
1111
|
+
// prepareAuth honours it before it ever reads the apikey. It cannot survive
|
|
1112
|
+
// validate: this port follows the Group A rule, so a stored null reads as
|
|
1113
|
+
// "no value" and the optspec's `auth` default fires instead - transmitting
|
|
1114
|
+
// the credential the caller withheld. Withhold the key for validate, then
|
|
1115
|
+
// put the null back. Same fix as ts/js/go makeOptions.
|
|
1116
|
+
//
|
|
1117
|
+
// mapget rather than getp: getp applies Group A and collapses a stored null
|
|
1118
|
+
// to undef, so it cannot tell an ABSENT auth from a suppressed one. And the
|
|
1119
|
+
// test is is_null, NOT is_nullish - a present-but-UNDEF auth is a missing
|
|
1120
|
+
// value, which the ts/js reference lets the optspec default fill in. Only an
|
|
1121
|
+
// explicit null is a suppression.
|
|
1122
|
+
bool authSuppressed = mapget(options, "auth").is_null();
|
|
1123
|
+
|
|
1110
1124
|
Value opts = Struct::clone(options);
|
|
1111
1125
|
|
|
1126
|
+
if (authSuppressed) {
|
|
1127
|
+
map_remove(opts, "auth");
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1112
1130
|
// Feature add-order. options.feature may be an ordered list of
|
|
1113
1131
|
// { name, active, ...opts } entries (the list position IS the order in which
|
|
1114
1132
|
// features are added), or a { name: {opts} } map. Normalize a list to a map
|
|
@@ -1159,6 +1177,11 @@ inline Value makeOptions(CtxPtr ctx) {
|
|
|
1159
1177
|
Value validated = Struct::validate(merged, optspec, vopts);
|
|
1160
1178
|
opts = validated;
|
|
1161
1179
|
|
|
1180
|
+
// Restore the suppression the optspec default would otherwise erase.
|
|
1181
|
+
if (authSuppressed) {
|
|
1182
|
+
map_put(opts, "auth", Value(nullptr));
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1162
1185
|
if (!is_nullish(sysFetch)) {
|
|
1163
1186
|
Value sys = Helpers::toMapAny(getp(opts, "system"));
|
|
1164
1187
|
if (sys.is_map()) {
|
|
@@ -85,8 +85,13 @@ public class CustomUtilityTest
|
|
|
85
85
|
// transport: 'base' - it REPLACES the transport by design - so a client
|
|
86
86
|
// in test mode would shadow the scripted fetcher and this would assert
|
|
87
87
|
// nothing.
|
|
88
|
+
// ["test"] is the OPTION, not the `test` FEATURE: it says "this
|
|
89
|
+
// client is not live", so a REQUIRED OpenAPI server variable resolves
|
|
90
|
+
// to a deterministic test-<name> instead of failing construction. It
|
|
91
|
+
// installs no transport, so the override under test still stands.
|
|
88
92
|
var client = new ProjectNameSDK(new Dictionary<string, object?>
|
|
89
93
|
{
|
|
94
|
+
["test"] = new Dictionary<string, object?> { ["active"] = true },
|
|
90
95
|
["utility"] = new Dictionary<string, object?> { ["fetcher"] = scripted },
|
|
91
96
|
});
|
|
92
97
|
|
|
@@ -122,8 +127,13 @@ public class CustomUtilityTest
|
|
|
122
127
|
};
|
|
123
128
|
};
|
|
124
129
|
|
|
130
|
+
// ["test"] is the OPTION, not the `test` FEATURE: it says "this
|
|
131
|
+
// client is not live", so a REQUIRED OpenAPI server variable resolves
|
|
132
|
+
// to a deterministic test-<name> instead of failing construction. It
|
|
133
|
+
// installs no transport, so the override under test still stands.
|
|
125
134
|
var client = new ProjectNameSDK(new Dictionary<string, object?>
|
|
126
135
|
{
|
|
136
|
+
["test"] = new Dictionary<string, object?> { ["active"] = true },
|
|
127
137
|
["utility"] = new Dictionary<string, object?> { ["fetcher"] = scripted },
|
|
128
138
|
});
|
|
129
139
|
|
|
@@ -141,8 +151,13 @@ public class CustomUtilityTest
|
|
|
141
151
|
[Fact]
|
|
142
152
|
public void AnUnknownKeyIsStillAttached()
|
|
143
153
|
{
|
|
154
|
+
// ["test"] is the OPTION, not the `test` FEATURE: it says "this
|
|
155
|
+
// client is not live", so a REQUIRED OpenAPI server variable resolves
|
|
156
|
+
// to a deterministic test-<name> instead of failing construction. It
|
|
157
|
+
// installs no transport, so the override under test still stands.
|
|
144
158
|
var client = new ProjectNameSDK(new Dictionary<string, object?>
|
|
145
159
|
{
|
|
160
|
+
["test"] = new Dictionary<string, object?> { ["active"] = true },
|
|
146
161
|
["utility"] = new Dictionary<string, object?>
|
|
147
162
|
{
|
|
148
163
|
["notAUtilityMember"] = Util("EXTRA"),
|
|
@@ -153,6 +153,51 @@ public static class TestRunner
|
|
|
153
153
|
return _testControl;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
// LiveClientOptions returns the extra SDK options every LIVE client is
|
|
157
|
+
// constructed with, read from sdk-test-control.json `test.client.options`.
|
|
158
|
+
//
|
|
159
|
+
// The generated live client knows two things: the base URL (from the
|
|
160
|
+
// spec) and the credential (from the environment). Everything else about
|
|
161
|
+
// how a particular API wants to be talked to - which features to switch
|
|
162
|
+
// on, and with what settings - is a property of THAT API, known to the
|
|
163
|
+
// project and to nothing in the toolchain.
|
|
164
|
+
//
|
|
165
|
+
// Merged UNDER the generated fields, so the suite's own
|
|
166
|
+
// base/apikey/server values win: this ADDS to the live client, it does
|
|
167
|
+
// not redirect it.
|
|
168
|
+
//
|
|
169
|
+
// Reserved fields are stripped HERE rather than at each merge site: the
|
|
170
|
+
// generated dictionary only names a field when the model calls for one,
|
|
171
|
+
// so a "base" in this block would face no competing value and would
|
|
172
|
+
// silently redirect the whole suite - credential included - to another
|
|
173
|
+
// host.
|
|
174
|
+
private static readonly string[] LiveReserved =
|
|
175
|
+
{ "base", "prefix", "suffix", "server", "apikey", "secret" };
|
|
176
|
+
|
|
177
|
+
public static Dictionary<string, object?> LiveClientOptions()
|
|
178
|
+
{
|
|
179
|
+
var ctrl = LoadTestControl();
|
|
180
|
+
if (!ctrl.TryGetValue("test", out var testRaw) ||
|
|
181
|
+
testRaw is not Dictionary<string, object?> test ||
|
|
182
|
+
!test.TryGetValue("client", out var clientRaw) ||
|
|
183
|
+
clientRaw is not Dictionary<string, object?> client ||
|
|
184
|
+
!client.TryGetValue("options", out var optsRaw) ||
|
|
185
|
+
optsRaw is not Dictionary<string, object?> opts)
|
|
186
|
+
{
|
|
187
|
+
return new Dictionary<string, object?>();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
var out_ = new Dictionary<string, object?>();
|
|
191
|
+
foreach (var kv in opts)
|
|
192
|
+
{
|
|
193
|
+
if (Array.IndexOf(LiveReserved, kv.Key) < 0)
|
|
194
|
+
{
|
|
195
|
+
out_[kv.Key] = kv.Value;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return out_;
|
|
199
|
+
}
|
|
200
|
+
|
|
156
201
|
// IsControlSkipped checks sdk-test-control.json for a skip entry.
|
|
157
202
|
// Returns (skip, reason).
|
|
158
203
|
public static (bool, string) IsControlSkipped(string kind, string name, string mode)
|
|
@@ -114,9 +114,26 @@ public static partial class SdkUtility
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
// `auth: null` is the documented way to disable auth outright, and
|
|
118
|
+
// PrepareAuth honours it before it ever reads the apikey. It cannot
|
|
119
|
+
// survive validate: depending on the struct port a stored null is
|
|
120
|
+
// either REPLACED by the optspec default - transmitting the credential
|
|
121
|
+
// the caller withheld - or REJECTED outright. Withhold the key for
|
|
122
|
+
// validate, then put the null back. Same fix as ts/js/go makeOptions.
|
|
123
|
+
//
|
|
124
|
+
// Suppliedness cannot be recovered after validate, hence here, and it
|
|
125
|
+
// must tell an ABSENT auth from a present null: TryGetValue rather
|
|
126
|
+
// than an indexer null check, which cannot distinguish them.
|
|
127
|
+
var authSuppressed = options.TryGetValue("auth", out var authval) && null == authval;
|
|
128
|
+
|
|
117
129
|
var opts = StructUtils.Clone(options) as Dictionary<string, object?>
|
|
118
130
|
?? new Dictionary<string, object?>();
|
|
119
131
|
|
|
132
|
+
if (authSuppressed)
|
|
133
|
+
{
|
|
134
|
+
opts.Remove("auth");
|
|
135
|
+
}
|
|
136
|
+
|
|
120
137
|
// Feature add-order. options.feature may be given as an ordered LIST of
|
|
121
138
|
// { name, active, ...opts } entries (the list position IS the order in
|
|
122
139
|
// which features are added), or as a { name: {opts} } map. Normalize a
|
|
@@ -230,6 +247,12 @@ public static partial class SdkUtility
|
|
|
230
247
|
var validated = StructUtils.Validate(merged, optspec);
|
|
231
248
|
opts = validated as Dictionary<string, object?> ?? new Dictionary<string, object?>();
|
|
232
249
|
|
|
250
|
+
// Restore the suppression the optspec default would otherwise erase.
|
|
251
|
+
if (authSuppressed)
|
|
252
|
+
{
|
|
253
|
+
opts["auth"] = null;
|
|
254
|
+
}
|
|
255
|
+
|
|
233
256
|
// Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
|
|
234
257
|
// Every placeholder must resolve to a non-empty value: from
|
|
235
258
|
// options.server (user), else the Config default. A placeholder that
|
|
@@ -106,12 +106,34 @@ dynamic makeOptions(dynamic ctx) {
|
|
|
106
106
|
mergeOptions['feature'] = fmap;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
// `auth: null` is the documented way to disable auth outright, and
|
|
110
|
+
// prepareAuth honours it before it ever reads the apikey. It cannot survive
|
|
111
|
+
// validate: depending on the struct port a stored null is either REPLACED
|
|
112
|
+
// by the optspec default — transmitting the credential the caller withheld
|
|
113
|
+
// — or REJECTED outright. Withhold the key for validate, then put the null
|
|
114
|
+
// back. Same fix as ts/js/go makeOptions.
|
|
115
|
+
//
|
|
116
|
+
// Suppliedness cannot be recovered after validate, hence here, and it must
|
|
117
|
+
// tell an ABSENT auth from a present null: containsKey rather than a null
|
|
118
|
+
// check on the value, which cannot distinguish them.
|
|
119
|
+
final authSuppressed =
|
|
120
|
+
options is Map && options.containsKey('auth') && null == options['auth'];
|
|
121
|
+
|
|
109
122
|
// User option maps are cloned first — their (possibly narrow) literal
|
|
110
123
|
// types must not constrain the merged structures.
|
|
111
124
|
dynamic opts = vs.merge([{}, cfgopts, vs.clone(mergeOptions)]);
|
|
112
125
|
|
|
126
|
+
if (authSuppressed && opts is Map) {
|
|
127
|
+
opts.remove('auth');
|
|
128
|
+
}
|
|
129
|
+
|
|
113
130
|
opts = vs.validate(opts, optspec);
|
|
114
131
|
|
|
132
|
+
// Restore the suppression the optspec default would otherwise erase.
|
|
133
|
+
if (authSuppressed && opts is Map) {
|
|
134
|
+
opts['auth'] = null;
|
|
135
|
+
}
|
|
136
|
+
|
|
115
137
|
// Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
|
|
116
138
|
// Every placeholder must resolve to a non-empty value: from options.server
|
|
117
139
|
// (user), else the Config default. A placeholder that resolves to '' is a
|