@voxgig/sdkgen 4.19.0 → 4.20.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/cmp/AgentGuideContent.js +1 -1
- package/dist/sdkgen.d.ts +2 -2
- package/dist/sdkgen.js +6 -3
- package/dist/sdkgen.js.map +1 -1
- package/dist/testkit.js +2 -2
- package/dist/testkit.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utility.d.ts +4 -1
- package/dist/utility.js +60 -0
- package/dist/utility.js.map +1 -1
- package/model/sdkgen.aon +10 -0
- package/package.json +1 -1
- package/project/.sdk/model/feature/feature-index.aon +20 -20
- package/project/.sdk/src/cmp/c/Config_c.ts +15 -0
- package/project/.sdk/src/cmp/c/Main_c.ts +15 -0
- package/project/.sdk/src/cmp/c/PrepareAuth_c.ts +371 -0
- package/project/.sdk/src/cmp/clojure/Config_clojure.ts +47 -1
- package/project/.sdk/src/cmp/clojure/Main_clojure.ts +11 -1
- package/project/.sdk/src/cmp/clojure/PrepareAuth_clojure.ts +374 -0
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +40 -0
- package/project/.sdk/src/cmp/cpp/Main_cpp.ts +17 -0
- package/project/.sdk/src/cmp/cpp/PrepareAuth_cpp.ts +441 -0
- package/project/.sdk/src/cmp/csharp/Config_csharp.ts +42 -1
- package/project/.sdk/src/cmp/csharp/Main_csharp.ts +18 -0
- package/project/.sdk/src/cmp/csharp/PrepareAuth_csharp.ts +392 -0
- package/project/.sdk/src/cmp/elixir/Config_elixir.ts +36 -1
- package/project/.sdk/src/cmp/elixir/Main_elixir.ts +15 -0
- package/project/.sdk/src/cmp/elixir/PrepareAuth_elixir.ts +418 -0
- package/project/.sdk/src/cmp/go/Config_go.ts +12 -1
- package/project/.sdk/src/cmp/go/Main_go.ts +14 -0
- package/project/.sdk/src/cmp/go/PrepareAuth_go.ts +322 -0
- package/project/.sdk/src/cmp/java/Config_java.ts +20 -1
- package/project/.sdk/src/cmp/java/Main_java.ts +14 -0
- package/project/.sdk/src/cmp/java/PrepareAuth_java.ts +298 -0
- package/project/.sdk/src/cmp/js/Config_js.ts +12 -1
- package/project/.sdk/src/cmp/js/Main_js.ts +6 -0
- package/project/.sdk/src/cmp/js/PrepareAuth_js.ts +244 -0
- package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +23 -1
- package/project/.sdk/src/cmp/kotlin/Main_kotlin.ts +20 -0
- package/project/.sdk/src/cmp/kotlin/PrepareAuth_kotlin.ts +440 -0
- package/project/.sdk/src/cmp/lua/Config_lua.ts +15 -1
- package/project/.sdk/src/cmp/lua/Main_lua.ts +11 -0
- package/project/.sdk/src/cmp/lua/PrepareAuth_lua.ts +364 -0
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +31 -1
- package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +19 -0
- package/project/.sdk/src/cmp/ocaml/PrepareAuth_ocaml.ts +454 -0
- package/project/.sdk/src/cmp/perl/Config_perl.ts +30 -0
- package/project/.sdk/src/cmp/perl/Main_perl.ts +14 -0
- package/project/.sdk/src/cmp/perl/PrepareAuth_perl.ts +387 -0
- package/project/.sdk/src/cmp/php/Config_php.ts +12 -1
- package/project/.sdk/src/cmp/php/Main_php.ts +7 -0
- package/project/.sdk/src/cmp/php/PrepareAuth_php.ts +325 -0
- package/project/.sdk/src/cmp/py/Config_py.ts +12 -1
- package/project/.sdk/src/cmp/py/Main_py.ts +12 -0
- package/project/.sdk/src/cmp/py/PrepareAuth_py.ts +389 -0
- package/project/.sdk/src/cmp/rb/Config_rb.ts +18 -1
- package/project/.sdk/src/cmp/rb/Main_rb.ts +10 -0
- package/project/.sdk/src/cmp/rb/PrepareAuth_rb.ts +305 -0
- package/project/.sdk/src/cmp/rust/Config_rust.ts +41 -1
- package/project/.sdk/src/cmp/rust/Main_rust.ts +16 -0
- package/project/.sdk/src/cmp/rust/PrepareAuth_rust.ts +410 -0
- package/project/.sdk/src/cmp/scala/Config_scala.ts +21 -1
- package/project/.sdk/src/cmp/scala/Main_scala.ts +14 -0
- package/project/.sdk/src/cmp/scala/PrepareAuth_scala.ts +415 -0
- package/project/.sdk/src/cmp/swift/Config_swift.ts +37 -1
- package/project/.sdk/src/cmp/swift/Main_swift.ts +10 -0
- package/project/.sdk/src/cmp/swift/PrepareAuth_swift.ts +439 -0
- package/project/.sdk/src/cmp/ts/Config_ts.ts +12 -1
- package/project/.sdk/src/cmp/ts/Main_ts.ts +5 -0
- package/project/.sdk/src/cmp/ts/PrepareAuth_ts.ts +237 -0
- package/project/.sdk/src/cmp/zig/Config_zig.ts +33 -1
- package/project/.sdk/src/cmp/zig/Main_zig.ts +16 -0
- package/project/.sdk/src/cmp/zig/PrepareAuth_zig.ts +545 -0
- package/project/.sdk/tm/clojure/src/sdk/core.clj +31 -20
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +36 -40
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +11 -0
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +19 -32
- package/project/.sdk/tm/java/utility/MakeOptions.java +13 -1
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +14 -1
- package/project/.sdk/tm/kotlin/utility/Prepare.kt +13 -46
- package/project/.sdk/tm/ocaml/Makefile +11 -1
- package/project/.sdk/tm/ocaml/sdk_helpers.ml +15 -0
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +31 -25
- package/project/.sdk/tm/ocaml/test/feature/secrets/t_secrets.ml +125 -12
- package/project/.sdk/tm/ocaml/test/t_pipeline.ml +64 -6
- package/project/.sdk/tm/perl/utility/make_options.pm +10 -1
- package/project/.sdk/tm/php/utility/MakeOptions.php +8 -1
- package/project/.sdk/tm/rust/utility/make_options.rs +13 -1
- package/project/.sdk/tm/scala/utility/Make.scala +14 -1
- package/project/.sdk/tm/scala/utility/Prepare.scala +16 -40
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +30 -3
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Prepare.swift +21 -39
- package/project/.sdk/tm/zig/core/utility.zig +37 -45
- package/project/.sdk/tm/zig/test/pipeline_test.zig +34 -0
- package/project/sdkgen-package.json +1 -1
- package/src/cmp/AgentGuideContent.ts +1 -1
- package/src/sdkgen.ts +4 -1
- package/src/testkit.ts +2 -2
- package/src/utility.ts +75 -0
- package/project/.sdk/tm/c/utility/prepare_auth.c +0 -65
- package/project/.sdk/tm/csharp/utility/PrepareAuth.cs +0 -54
- package/project/.sdk/tm/go/utility/prepare_auth.go +0 -59
- package/project/.sdk/tm/java/utility/PrepareAuth.java +0 -66
- package/project/.sdk/tm/js/src/utility/PrepareAuthUtility.js +0 -49
- package/project/.sdk/tm/lua/utility/prepare_auth.lua +0 -52
- package/project/.sdk/tm/perl/utility/prepare_auth.pm +0 -56
- package/project/.sdk/tm/php/utility/PrepareAuth.php +0 -45
- package/project/.sdk/tm/py/pkg/utility/prepare_auth.py +0 -46
- package/project/.sdk/tm/rb/utility/prepare_auth.rb +0 -35
- package/project/.sdk/tm/rust/utility/prepare_auth.rs +0 -65
- package/project/.sdk/tm/ts/src/utility/PrepareAuthUtility.ts +0 -78
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
|
|
2
|
+
import {
|
|
3
|
+
Content,
|
|
4
|
+
File,
|
|
5
|
+
Folder,
|
|
6
|
+
cmp,
|
|
7
|
+
isHttpBasicAuth,
|
|
8
|
+
resolveAuthIn,
|
|
9
|
+
resolveAuthName,
|
|
10
|
+
} from '@voxgig/sdkgen'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
KIT,
|
|
15
|
+
getModelPath,
|
|
16
|
+
} from '@voxgig/apidef'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
// WHERE THE CREDENTIAL GOES IS A FACT ABOUT THE API, so it is generated
|
|
20
|
+
// rather than templated.
|
|
21
|
+
//
|
|
22
|
+
// This was a static file at `tm/rb/utility/prepare_auth.rb` that hardcoded
|
|
23
|
+
// `HEADER_AUTH = "authorization"`. apidef has always resolved the scheme's
|
|
24
|
+
// `in` and `name` into `main.kit.info.security` — joplin's says
|
|
25
|
+
// `in: "query", name: "token"` — and generation dropped both. The result
|
|
26
|
+
// was an SDK that sent a header the API does not read and never sent the
|
|
27
|
+
// query parameter it does, so it could not authenticate at all. Four repos
|
|
28
|
+
// in the cedar fleet shipped that way: joplin (`token`), pipedrive
|
|
29
|
+
// (`api_token`), trello (`key`), lm-umbrella (`apiKey`).
|
|
30
|
+
//
|
|
31
|
+
// A template cannot fix this, because the three placements need three
|
|
32
|
+
// different bodies and a template has to pick one. A component emits the
|
|
33
|
+
// branch this API actually uses and nothing else — no dead query code in a
|
|
34
|
+
// bearer-token SDK, and no runtime `if` on a value that is fixed at
|
|
35
|
+
// generation time.
|
|
36
|
+
//
|
|
37
|
+
// The ts port is the reference (cmp/ts/PrepareAuth_ts.ts). Two things are
|
|
38
|
+
// deliberately NOT the same here, both recorded at their site below: the
|
|
39
|
+
// header name is lower-cased, and the cookie clear removes our own pair
|
|
40
|
+
// instead of a header that was never set.
|
|
41
|
+
const PrepareAuth = cmp(async function PrepareAuth(props: any) {
|
|
42
|
+
const { target } = props
|
|
43
|
+
const { model } = props.ctx$
|
|
44
|
+
|
|
45
|
+
const active = authSwitchedOn(model)
|
|
46
|
+
const where = resolveAuthIn(model)
|
|
47
|
+
const name = resolveAuthName(model)
|
|
48
|
+
const basic = isHttpBasicAuth(model)
|
|
49
|
+
|
|
50
|
+
// RB'S LAYOUT IS FLAT, WHICH IS WHY THIS FOLDER IS OPENED HERE AND THE TS
|
|
51
|
+
// PORT OPENS NONE. In ts, Main wraps its whole body in
|
|
52
|
+
// `Folder({name:'src'})` and the utilities live at `src/utility/`, so a
|
|
53
|
+
// second `src` there would write `src/src/utility/`. Main_rb opens no such
|
|
54
|
+
// folder: `Package`, `Gitignore`, the SDK file and `Copy({from:'tm/rb'})`
|
|
55
|
+
// all land at the target root, and `Config` sits inside a `Folder({name:
|
|
56
|
+
// '.'})` that is the root too. The template this replaces was
|
|
57
|
+
// `tm/rb/utility/prepare_auth.rb`, and that Copy's root IS the target root,
|
|
58
|
+
// so the file it produced was `<root>/utility/prepare_auth.rb`. One folder,
|
|
59
|
+
// `utility`, reproduces exactly that path — and register.rb's
|
|
60
|
+
// `require_relative 'prepare_auth'` (from `utility/`) only resolves there.
|
|
61
|
+
Folder({ name: 'utility' }, () => {
|
|
62
|
+
File({ name: 'prepare_auth.' + target.ext }, () => {
|
|
63
|
+
Content(render({ Name: model.const.Name, active, where, name, basic }))
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
function render(spec: {
|
|
70
|
+
Name: string, active: boolean, where: string, name: string, basic: boolean
|
|
71
|
+
}): string {
|
|
72
|
+
const head = `# ${spec.Name} SDK utility: prepare_auth
|
|
73
|
+
require_relative 'struct/voxgig_struct'
|
|
74
|
+
module ${spec.Name}Utilities
|
|
75
|
+
`
|
|
76
|
+
|
|
77
|
+
const guard = ` PrepareAuth = ->(ctx) {
|
|
78
|
+
spec = ctx.spec
|
|
79
|
+
return nil, ctx.make_error("auth_no_spec", "Expected context spec property to be defined.") unless spec
|
|
80
|
+
`
|
|
81
|
+
|
|
82
|
+
// NO AUTH AT ALL. A public API's SDK gets a prepare_auth that is honest
|
|
83
|
+
// about it rather than one that deletes a header nobody set.
|
|
84
|
+
if (!spec.active) {
|
|
85
|
+
return head + ` # This API declares no authentication, so there is no credential to
|
|
86
|
+
# place. The utility stays in the pipeline because make_spec calls it
|
|
87
|
+
# unconditionally, and register.rb binds it unconditionally.
|
|
88
|
+
${guard}
|
|
89
|
+
return spec, nil
|
|
90
|
+
}
|
|
91
|
+
end
|
|
92
|
+
`
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const cookie = 'cookie' === spec.where
|
|
96
|
+
const query = 'query' === spec.where
|
|
97
|
+
|
|
98
|
+
// HTTP Basic is header-only by definition: the scheme is
|
|
99
|
+
// `Authorization: Basic base64(user:pass)`. It cannot be expressed as a
|
|
100
|
+
// query parameter or a cookie, so the branch is emitted only where it can
|
|
101
|
+
// mean something.
|
|
102
|
+
const basicHere = spec.basic && 'header' === spec.where
|
|
103
|
+
|
|
104
|
+
// The constant keeps the template's name per placement, so a
|
|
105
|
+
// header-based SDK regenerates the file it already had.
|
|
106
|
+
const CRED = query ? 'QUERY_AUTH' : cookie ? 'COOKIE_AUTH' : 'HEADER_AUTH'
|
|
107
|
+
|
|
108
|
+
// HEADER NAMES ARE LOWER-CASED; QUERY AND COOKIE NAMES ARE NOT.
|
|
109
|
+
//
|
|
110
|
+
// `resolveAuthName` answers 'Authorization' by default, but the whole rb
|
|
111
|
+
// runtime spells header names in lower case — the template's
|
|
112
|
+
// `HEADER_AUTH = "authorization"`, secrets_feature.rb's
|
|
113
|
+
// `headers["authorization"]`, debug_feature.rb's mask list. A Ruby Hash key
|
|
114
|
+
// is case-sensitive, so emitting "Authorization" here would leave the
|
|
115
|
+
// secrets feature writing a SECOND, separate header and the copied
|
|
116
|
+
// pipeline_test.rb assertions on `headers["authorization"]` failing. HTTP
|
|
117
|
+
// header names are case-insensitive on the wire, so lower-casing costs
|
|
118
|
+
// nothing. Query parameter and cookie names ARE case-sensitive (`?Token=`
|
|
119
|
+
// is not `?token=`), so those go in verbatim.
|
|
120
|
+
const credName = query || cookie ? spec.name : spec.name.toLowerCase()
|
|
121
|
+
|
|
122
|
+
const consts =
|
|
123
|
+
` ${CRED} = ${rbstr(credName)}
|
|
124
|
+
` +
|
|
125
|
+
(cookie ? ` HEADER_COOKIE = "cookie"
|
|
126
|
+
` : '') +
|
|
127
|
+
` OPTION_APIKEY = "apikey"
|
|
128
|
+
` +
|
|
129
|
+
(basicHere ? ` OPTION_SECRET = "secret"
|
|
130
|
+
` : '') +
|
|
131
|
+
` NOT_FOUND = "__NOTFOUND__"
|
|
132
|
+
|
|
133
|
+
`
|
|
134
|
+
|
|
135
|
+
// The bag the credential lands in. A cookie rides the header bag, because
|
|
136
|
+
// a cookie IS a header.
|
|
137
|
+
const bag = query
|
|
138
|
+
? ` query = spec.query
|
|
139
|
+
`
|
|
140
|
+
: ` headers = spec.headers
|
|
141
|
+
`
|
|
142
|
+
|
|
143
|
+
// Removing OUR pair, not the whole cookie header. The ts port deletes
|
|
144
|
+
// `headers[CRED_name]` for the cookie case, which is a header nothing ever
|
|
145
|
+
// set — so a stale cookie survives a withdrawn credential there. This
|
|
146
|
+
// splits the header, drops the one pair we own, and puts the rest back.
|
|
147
|
+
const dropCookie = !cookie ? '' : `
|
|
148
|
+
# Our own pair, and only ours: another cookie the caller set survives.
|
|
149
|
+
drop_cookie = ->(hs) {
|
|
150
|
+
cookie = hs[HEADER_COOKIE]
|
|
151
|
+
return unless cookie.is_a?(String)
|
|
152
|
+
rest = cookie.split("; ").reject { |pair| pair.start_with?("#{COOKIE_AUTH}=") }
|
|
153
|
+
if rest.empty?
|
|
154
|
+
hs.delete(HEADER_COOKIE)
|
|
155
|
+
else
|
|
156
|
+
hs[HEADER_COOKIE] = rest.join("; ")
|
|
157
|
+
end
|
|
158
|
+
}
|
|
159
|
+
`
|
|
160
|
+
|
|
161
|
+
const clear = cookie ? 'drop_cookie.call(headers)'
|
|
162
|
+
: query ? `query.delete(${CRED})`
|
|
163
|
+
: `headers.delete(${CRED})`
|
|
164
|
+
|
|
165
|
+
const preamble = guard + `
|
|
166
|
+
` + bag + ` options = ctx.client.options_map
|
|
167
|
+
` + dropCookie + `
|
|
168
|
+
# Public APIs that need no auth omit the options.auth block entirely.
|
|
169
|
+
if options["auth"].nil?
|
|
170
|
+
${clear}
|
|
171
|
+
return spec, nil
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
apikey = VoxgigStruct.getprop(options, OPTION_APIKEY, NOT_FOUND)
|
|
175
|
+
`
|
|
176
|
+
|
|
177
|
+
const basicBlock = !basicHere ? '' : `
|
|
178
|
+
# True HTTP Basic Auth needs TWO credentials, base64-joined - a single
|
|
179
|
+
# token in the header (the branch below) can never authenticate against
|
|
180
|
+
# an API that actually checks \`Authorization: Basic base64(user:pass)\`.
|
|
181
|
+
if VoxgigStruct.getpath(options, "auth.basic") == true
|
|
182
|
+
secret = VoxgigStruct.getprop(options, OPTION_SECRET, NOT_FOUND)
|
|
183
|
+
no_apikey = apikey.nil? || !apikey.is_a?(String) || apikey == NOT_FOUND || apikey == ""
|
|
184
|
+
no_secret = secret.nil? || !secret.is_a?(String) || secret == NOT_FOUND || secret == ""
|
|
185
|
+
|
|
186
|
+
if no_apikey || no_secret
|
|
187
|
+
headers.delete(HEADER_AUTH)
|
|
188
|
+
else
|
|
189
|
+
auth_prefix = VoxgigStruct.getpath(options, "auth.prefix") || ""
|
|
190
|
+
# \`pack("m0")\` rather than \`Base64.strict_encode64\`: base64 left
|
|
191
|
+
# Ruby's default gems in 3.4, and pack is core.
|
|
192
|
+
b64 = ["#{apikey}:#{secret}"].pack("m0")
|
|
193
|
+
headers[HEADER_AUTH] =
|
|
194
|
+
auth_prefix.empty? ? b64 : "#{auth_prefix} #{b64}"
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
return spec, nil
|
|
198
|
+
end
|
|
199
|
+
`
|
|
200
|
+
|
|
201
|
+
return head + consts + preamble + basicBlock + place(spec.where, clear) + `
|
|
202
|
+
return spec, nil
|
|
203
|
+
}
|
|
204
|
+
end
|
|
205
|
+
`
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
// The one thing that actually differs between placements: where the
|
|
210
|
+
// credential is written, and whether the prefix travels with it.
|
|
211
|
+
function place(where: string, clear: string): string {
|
|
212
|
+
// NO PREFIX IN A QUERY STRING. `?token=Bearer%20abc` is not a thing any
|
|
213
|
+
// API reads; the prefix is a header convention and is dropped here
|
|
214
|
+
// deliberately rather than silently concatenated.
|
|
215
|
+
if ('query' === where) {
|
|
216
|
+
return `
|
|
217
|
+
if apikey.nil? || (apikey.is_a?(String) && (apikey == NOT_FOUND || apikey == ""))
|
|
218
|
+
${clear}
|
|
219
|
+
else
|
|
220
|
+
apikey_val = apikey.is_a?(String) ? apikey : ""
|
|
221
|
+
# NO PREFIX IN A QUERY STRING: \`?token=Bearer%20abc\` is not a thing
|
|
222
|
+
# any API reads, so options.auth.prefix is dropped rather than joined.
|
|
223
|
+
query[QUERY_AUTH] = apikey_val
|
|
224
|
+
end
|
|
225
|
+
`
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if ('cookie' === where) {
|
|
229
|
+
return `
|
|
230
|
+
# Dropped before writing, so a retry cannot accumulate the pair and a
|
|
231
|
+
# withdrawn credential leaves no stale cookie behind.
|
|
232
|
+
${clear}
|
|
233
|
+
|
|
234
|
+
unless apikey.nil? || (apikey.is_a?(String) && (apikey == NOT_FOUND || apikey == ""))
|
|
235
|
+
apikey_val = apikey.is_a?(String) ? apikey : ""
|
|
236
|
+
# A cookie IS a header, so the pair is appended to the cookie header
|
|
237
|
+
# rather than clobbering it. No prefix: \`token=Bearer abc\` is not a
|
|
238
|
+
# cookie value any API reads.
|
|
239
|
+
existing = headers[HEADER_COOKIE]
|
|
240
|
+
pair = "#{COOKIE_AUTH}=#{apikey_val}"
|
|
241
|
+
headers[HEADER_COOKIE] =
|
|
242
|
+
(existing.is_a?(String) && !existing.empty?) ? "#{existing}; #{pair}" : pair
|
|
243
|
+
end
|
|
244
|
+
`
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// HEADER: the template's body, unchanged.
|
|
248
|
+
return `
|
|
249
|
+
if apikey.nil? || (apikey.is_a?(String) && (apikey == NOT_FOUND || apikey == ""))
|
|
250
|
+
${clear}
|
|
251
|
+
else
|
|
252
|
+
auth_prefix = VoxgigStruct.getpath(options, "auth.prefix") || ""
|
|
253
|
+
apikey_val = apikey.is_a?(String) ? apikey : ""
|
|
254
|
+
# Empty prefix (raw apiKey credential) must not add a leading space.
|
|
255
|
+
headers[HEADER_AUTH] =
|
|
256
|
+
auth_prefix.empty? ? apikey_val : "#{auth_prefix} #{apikey_val}"
|
|
257
|
+
end
|
|
258
|
+
`
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
// NOT `isAuthActive`, AND THE DIFFERENCE IS LOAD-BEARING (the py port found
|
|
263
|
+
// this first; same reasoning, same fix).
|
|
264
|
+
//
|
|
265
|
+
// `isAuthActive` is false whenever the SPEC declares no security scheme
|
|
266
|
+
// (`main.kit.info.auth: false`). That is a statement about the DEFINITION,
|
|
267
|
+
// not a ban on ever sending a credential: apidef writes it for every spec
|
|
268
|
+
// with no securitySchemes block, and those SDKs are still expected to honour
|
|
269
|
+
// an `apikey` the caller passes. The template placed the credential
|
|
270
|
+
// unconditionally, so they did.
|
|
271
|
+
//
|
|
272
|
+
// Gating the body on `isAuthActive` therefore does not trim dead code, it
|
|
273
|
+
// deletes working authentication. MEASURED, not assumed: with the wider gate
|
|
274
|
+
// this component emitted the no-op for generatedcompile's own fixture
|
|
275
|
+
// (`main: kit: info: { ... auth: false }`) and two running rb lanes went red
|
|
276
|
+
// —
|
|
277
|
+
//
|
|
278
|
+
// rb: auth null beats an explicit apikey
|
|
279
|
+
// FAIL: baseline broken: an ordinary apikey was not sent
|
|
280
|
+
// rb: the secrets feature runs with the feature active
|
|
281
|
+
// expected the authorization header to carry OPTKEY01, got: nil
|
|
282
|
+
//
|
|
283
|
+
// So the no-op is emitted only when the PROJECT says so: `config.auth.active:
|
|
284
|
+
// false`, an explicit per-SDK switch nobody sets by accident. A spec that is
|
|
285
|
+
// merely silent keeps the credential path it has always had.
|
|
286
|
+
function authSwitchedOn(model: any): boolean {
|
|
287
|
+
const auth = getModelPath(model, `main.${KIT}.config.auth`,
|
|
288
|
+
{ only_active: false, required: false })
|
|
289
|
+
return !(null != auth && false === auth.active)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
// A Ruby double-quoted literal. `#` is escaped only where it would OPEN an
|
|
294
|
+
// interpolation, matching utility_rb.formatRubyValue.
|
|
295
|
+
function rbstr(s: string): string {
|
|
296
|
+
return '"' + String(s)
|
|
297
|
+
.replace(/\\/g, '\\\\')
|
|
298
|
+
.replace(/"/g, '\\"')
|
|
299
|
+
.replace(/#(?=[{$@])/g, '\\#') + '"'
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
export {
|
|
304
|
+
PrepareAuth
|
|
305
|
+
}
|
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
each,
|
|
9
9
|
isAuthActive,
|
|
10
10
|
isConfigData,
|
|
11
|
+
resolveAuthIn,
|
|
12
|
+
resolveAuthName,
|
|
11
13
|
resolveAuthPrefix,
|
|
12
14
|
targetFeatures,
|
|
13
15
|
} from '@voxgig/sdkgen'
|
|
@@ -58,7 +60,45 @@ const Config = cmp(async function Config(props: any) {
|
|
|
58
60
|
// slug/version/target identity fields (station descriptor input, mirrors
|
|
59
61
|
// Config_ts) - both reps below render from this same def, so the data
|
|
60
62
|
// and literal branches pick the fields up together.
|
|
61
|
-
const { def: config, json:
|
|
63
|
+
const { def: config, json: baseJson } = configDefinition(model, target.name)
|
|
64
|
+
|
|
65
|
+
// `in` and `name` TRAVEL WITH THE PREFIX NOW.
|
|
66
|
+
//
|
|
67
|
+
// apidef resolved both from the spec's securityScheme all along and this
|
|
68
|
+
// generator dropped them, so an apiKey-in-query API got an `authorization`
|
|
69
|
+
// header it does not read (see PrepareAuth_rust). The generated
|
|
70
|
+
// prepare_auth is built from the same two values, and the config carries
|
|
71
|
+
// them so the placement is a visible, overridable runtime option beside
|
|
72
|
+
// auth.prefix - which is also what keeps the SDK from rejecting its own
|
|
73
|
+
// config: the auth optspec in tm/rust/utility/make_options.rs is a CLOSED
|
|
74
|
+
// map, and an undeclared key there fails validate with "Unexpected keys at
|
|
75
|
+
// field auth".
|
|
76
|
+
//
|
|
77
|
+
// Emitted ONLY when they differ from header/Authorization, so a
|
|
78
|
+
// header-based SDK's config.rs is byte-identical to what it generated
|
|
79
|
+
// before. Both representations are patched through the one object, so the
|
|
80
|
+
// literal (formatRustValue) and the data rep (rustRawString) cannot
|
|
81
|
+
// disagree - the json is re-derived from the def, exactly as
|
|
82
|
+
// configDefinition derives it.
|
|
83
|
+
const authIn = resolveAuthIn(model)
|
|
84
|
+
const authName = resolveAuthName(model)
|
|
85
|
+
let configJson = baseJson
|
|
86
|
+
|
|
87
|
+
if (authActive && null != config.options && null != config.options.auth) {
|
|
88
|
+
let changed = false
|
|
89
|
+
if ('header' !== authIn) {
|
|
90
|
+
config.options.auth.in = authIn
|
|
91
|
+
changed = true
|
|
92
|
+
}
|
|
93
|
+
if ('Authorization' !== authName) {
|
|
94
|
+
config.options.auth.name = authName
|
|
95
|
+
changed = true
|
|
96
|
+
}
|
|
97
|
+
if (changed) {
|
|
98
|
+
configJson = JSON.stringify(config)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
62
102
|
const asData = isConfigData(configJson, configReprSetting(model))
|
|
63
103
|
|
|
64
104
|
File({ name: 'config.' + target.ext }, () => {
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
|
|
25
25
|
import { Package } from './Package_rust'
|
|
26
26
|
import { Config } from './Config_rust'
|
|
27
|
+
import { PrepareAuth } from './PrepareAuth_rust'
|
|
27
28
|
import { Gitignore } from './Gitignore_rust'
|
|
28
29
|
import { MainEntity } from './MainEntity_rust'
|
|
29
30
|
import { EntityBase } from './EntityBase_rust'
|
|
@@ -67,6 +68,21 @@ const Main = cmp(async function Main(props: any) {
|
|
|
67
68
|
}
|
|
68
69
|
})
|
|
69
70
|
|
|
71
|
+
// utility/prepare_auth.rs is GENERATED, not templated: where the
|
|
72
|
+
// credential goes (header / query / cookie, and under what name) is a
|
|
73
|
+
// fact about the API, and tm/ can only hold one answer. See
|
|
74
|
+
// PrepareAuth_rust.
|
|
75
|
+
//
|
|
76
|
+
// AT ROOT LEVEL, and outside the `core` Folder below. The rust crate root
|
|
77
|
+
// IS the target root - lib.rs, core/, feature/ and utility/ all sit there
|
|
78
|
+
// - the component opens `utility` itself, and that is the same path
|
|
79
|
+
// `Copy({from:'tm/rust'})` used for the template it replaces. Calling it
|
|
80
|
+
// beside Config, inside `Folder({name:'core'})`, would emit
|
|
81
|
+
// core/utility/prepare_auth.rs instead: a file no module declares, so
|
|
82
|
+
// rustc never compiles it, while utility/mod.rs's `pub mod prepare_auth;`
|
|
83
|
+
// keeps binding whatever utility/ actually holds.
|
|
84
|
+
PrepareAuth({ target })
|
|
85
|
+
|
|
70
86
|
// Generated core files: the client (sdk.rs), the API config and the
|
|
71
87
|
// branded error type.
|
|
72
88
|
Folder({ name: 'core' }, () => {
|