@voxgig/sdkgen 4.19.1 → 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/sdkgen.d.ts +2 -2
- package/dist/sdkgen.js +6 -3
- package/dist/sdkgen.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/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/sdkgen.ts +4 -1
- 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,325 @@
|
|
|
1
|
+
|
|
2
|
+
import {
|
|
3
|
+
Content,
|
|
4
|
+
File,
|
|
5
|
+
Folder,
|
|
6
|
+
cmp,
|
|
7
|
+
isHttpBasicAuth,
|
|
8
|
+
resolveAuthIn,
|
|
9
|
+
resolveAuthName,
|
|
10
|
+
resolveAuthPrefix,
|
|
11
|
+
} from '@voxgig/sdkgen'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
KIT,
|
|
16
|
+
getModelPath,
|
|
17
|
+
} from '@voxgig/apidef'
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
// WHERE THE CREDENTIAL GOES IS A FACT ABOUT THE API, so it is generated
|
|
21
|
+
// rather than templated.
|
|
22
|
+
//
|
|
23
|
+
// This was a static file in `tm/php/utility/` that hardcoded an
|
|
24
|
+
// `authorization` HEADER. apidef has always resolved the scheme's `in` and
|
|
25
|
+
// `name` into `main.kit.info.security` — joplin's says
|
|
26
|
+
// `in: "query", name: "token"` — and generation dropped both. The result
|
|
27
|
+
// was an SDK that sent a header the API does not read and never sent the
|
|
28
|
+
// query parameter it does, so it could not authenticate at all. Four repos
|
|
29
|
+
// in the cedar fleet shipped that way: joplin (`token`), pipedrive
|
|
30
|
+
// (`api_token`), trello (`key`), lm-umbrella (`apiKey`).
|
|
31
|
+
//
|
|
32
|
+
// A template cannot fix this, because the three placements need three
|
|
33
|
+
// different bodies and a template has to pick one. A component emits the
|
|
34
|
+
// branch this API actually uses and nothing else — no dead query code in a
|
|
35
|
+
// bearer-token SDK, and no runtime `if` on a value that is fixed at
|
|
36
|
+
// generation time.
|
|
37
|
+
//
|
|
38
|
+
// The php peer of cmp/ts/PrepareAuth_ts.ts.
|
|
39
|
+
const PrepareAuth = cmp(async function PrepareAuth(props: any) {
|
|
40
|
+
const { target } = props
|
|
41
|
+
const { model } = props.ctx$
|
|
42
|
+
|
|
43
|
+
// FOLDER NESTING. Main_php opens NO folder around its children — it
|
|
44
|
+
// writes `<sdk>_sdk.php`, `features.php` and (inside `Folder('.')`)
|
|
45
|
+
// `config.php` straight into the target root, and each sibling component
|
|
46
|
+
// opens its own single level (EntityTypes -> `types/`, Entity ->
|
|
47
|
+
// `entity/`, Test -> `test/`). The template this replaces lived at
|
|
48
|
+
// `tm/php/utility/PrepareAuth.php` and the blanket Copy puts `tm/php/*`
|
|
49
|
+
// at the target root, so the generated file belongs at `utility/`, one
|
|
50
|
+
// folder deep. (This is NOT the ts layout: there Main already opens
|
|
51
|
+
// `src`, so PrepareAuth_ts must not open a second one.)
|
|
52
|
+
Folder({ name: 'utility' }, () => {
|
|
53
|
+
File({ name: 'PrepareAuth.' + target.ext }, () => {
|
|
54
|
+
Content(render({
|
|
55
|
+
Name: model.const.Name,
|
|
56
|
+
active: authSwitchedOn(model),
|
|
57
|
+
where: resolveAuthIn(model),
|
|
58
|
+
name: resolveAuthName(model),
|
|
59
|
+
basic: isHttpBasicAuth(model),
|
|
60
|
+
// Read so the resolution is exercised here too, even though the
|
|
61
|
+
// prefix is a RUNTIME option (`options.auth.prefix`, emitted by
|
|
62
|
+
// Config_php) rather than something baked into this file.
|
|
63
|
+
prefix: resolveAuthPrefix(model),
|
|
64
|
+
}))
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
// NOT `isAuthActive`, AND THE DIFFERENCE IS LOAD-BEARING (the py port found
|
|
71
|
+
// this first; same reasoning, same fix, and php's own lane proves it here).
|
|
72
|
+
//
|
|
73
|
+
// `isAuthActive` is false whenever the SPEC declares no security scheme
|
|
74
|
+
// (`main.kit.info.auth: false`). That is a statement about the DEFINITION,
|
|
75
|
+
// not a ban on ever sending a credential: apidef writes it for every spec
|
|
76
|
+
// with no securitySchemes block, GitHub's official OpenAPI included, and
|
|
77
|
+
// those SDKs are still expected to honour an `apikey` the caller passes.
|
|
78
|
+
// The template placed the credential unconditionally, so they did.
|
|
79
|
+
//
|
|
80
|
+
// Gating the body on `isAuthActive` therefore does not trim dead code, it
|
|
81
|
+
// deletes working authentication. MEASURED, not assumed: with the wider gate
|
|
82
|
+
// this component emitted the no-op for generatedcompile's own fixture
|
|
83
|
+
// (`main: kit: info: { ... auth: false }`) and the php lane went red —
|
|
84
|
+
//
|
|
85
|
+
// php: auth null beats an explicit apikey
|
|
86
|
+
// FAIL: baseline broken: an ordinary apikey was not sent
|
|
87
|
+
//
|
|
88
|
+
// So the no-op is emitted only when the PROJECT says so:
|
|
89
|
+
// `config.auth.active: false`, an explicit per-SDK switch nobody sets by
|
|
90
|
+
// accident. A spec that is merely silent keeps the credential path it has
|
|
91
|
+
// always had.
|
|
92
|
+
function authSwitchedOn(model: any): boolean {
|
|
93
|
+
const auth = getModelPath(model, `main.${KIT}.config.auth`,
|
|
94
|
+
{ only_active: false, required: false })
|
|
95
|
+
return !(null != auth && false === auth.active)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
type AuthSpec = {
|
|
100
|
+
Name: string,
|
|
101
|
+
active: boolean,
|
|
102
|
+
where: string,
|
|
103
|
+
name: string,
|
|
104
|
+
basic: boolean,
|
|
105
|
+
prefix: string,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
function render(spec: AuthSpec): string {
|
|
110
|
+
const Name = spec.Name
|
|
111
|
+
|
|
112
|
+
const head = `<?php
|
|
113
|
+
declare(strict_types=1);
|
|
114
|
+
|
|
115
|
+
// ${Name} SDK utility: prepare_auth
|
|
116
|
+
|
|
117
|
+
`
|
|
118
|
+
|
|
119
|
+
// AUTH SWITCHED OFF BY THE PROJECT (see authSwitchedOn for why only an
|
|
120
|
+
// EXPLICIT switch counts). The SDK gets a prepare_auth that is honest
|
|
121
|
+
// about it rather than one that deletes a header nobody set.
|
|
122
|
+
if (!spec.active) {
|
|
123
|
+
return head + `// This SDK is configured with authentication off, so there is no
|
|
124
|
+
// credential to place. The class stays in the pipeline because make_spec
|
|
125
|
+
// calls prepare_auth unconditionally.
|
|
126
|
+
class ${Name}PrepareAuth
|
|
127
|
+
{
|
|
128
|
+
public static function call(${Name}Context $ctx): array
|
|
129
|
+
{
|
|
130
|
+
$spec = $ctx->spec;
|
|
131
|
+
if (!$spec) {
|
|
132
|
+
return [null, $ctx->make_error('auth_no_spec', 'Expected context spec property to be defined.')];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return [$spec, null];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
`
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const bag = bagVar(spec.where)
|
|
142
|
+
|
|
143
|
+
return head + `class ${Name}PrepareAuth
|
|
144
|
+
{
|
|
145
|
+
${constants(spec)}
|
|
146
|
+
public static function call(${Name}Context $ctx): array
|
|
147
|
+
{
|
|
148
|
+
$spec = $ctx->spec;
|
|
149
|
+
if (!$spec) {
|
|
150
|
+
return [null, $ctx->make_error('auth_no_spec', 'Expected context spec property to be defined.')];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
$${bag} = &$spec->${bag};
|
|
154
|
+
$options = $ctx->client->options_map();
|
|
155
|
+
|
|
156
|
+
// Public APIs that need no auth omit the options.auth block entirely.
|
|
157
|
+
if (!isset($options['auth']) || $options['auth'] === null) {
|
|
158
|
+
${suppressed(spec.where)} return [$spec, null];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
$apikey = \\Voxgig\\Struct\\Struct::getprop($options, self::OPTION_APIKEY, self::NOT_FOUND);
|
|
162
|
+
${basicBlock(spec)}${placeBlock(spec)}
|
|
163
|
+
return [$spec, null];
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
`
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
// The bag the credential lands in, per placement. Cookies ride the header
|
|
171
|
+
// bag because a cookie IS a header.
|
|
172
|
+
function bagVar(where: string): string {
|
|
173
|
+
return 'query' === where ? 'query' : 'headers'
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
// The constant holding the credential's NAME, per placement. `HEADER_AUTH`
|
|
178
|
+
// keeps the name the template used, so a header SDK regenerates unchanged.
|
|
179
|
+
function credConst(where: string): string {
|
|
180
|
+
return 'query' === where ? 'QUERY_AUTH' : 'cookie' === where ? 'COOKIE_AUTH' : 'HEADER_AUTH'
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
function constants(spec: AuthSpec): string {
|
|
185
|
+
// A HEADER NAME GOES IN LOWERCASE, as every target's template has always
|
|
186
|
+
// written it (`authorization`): request header names are case-insensitive,
|
|
187
|
+
// and this SDK's own response headers are lowercased by the fetcher, so
|
|
188
|
+
// lowercasing here keeps one spelling throughout — and keeps the default
|
|
189
|
+
// byte-identical to the template this replaces. A query parameter and a
|
|
190
|
+
// cookie name are CASE-SENSITIVE, so those go in verbatim.
|
|
191
|
+
const name = 'header' === spec.where ? spec.name.toLowerCase() : spec.name
|
|
192
|
+
|
|
193
|
+
const cookieConst = 'cookie' === spec.where ?
|
|
194
|
+
` private const HEADER_COOKIE = 'cookie';\n` : ''
|
|
195
|
+
|
|
196
|
+
const secretConst = basicActive(spec) ?
|
|
197
|
+
` private const OPTION_SECRET = 'secret';\n` : ''
|
|
198
|
+
|
|
199
|
+
return ` private const ${credConst(spec.where)} = ${phpstr(name)};
|
|
200
|
+
${cookieConst} private const OPTION_APIKEY = 'apikey';
|
|
201
|
+
${secretConst} private const NOT_FOUND = '__NOTFOUND__';
|
|
202
|
+
`
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
// The `options.auth` block is absent (`auth: null`) — drop any stale
|
|
207
|
+
// credential the caller's own options put in the bag.
|
|
208
|
+
function suppressed(where: string): string {
|
|
209
|
+
if ('cookie' === where) {
|
|
210
|
+
// NOTHING TO CLEAR. The cookie header may carry the caller's own
|
|
211
|
+
// cookies, and this SDK has not written a pair of its own into this
|
|
212
|
+
// spec, so removing the header would throw away someone else's state.
|
|
213
|
+
return ''
|
|
214
|
+
}
|
|
215
|
+
return ` unset($${bagVar(where)}[self::${credConst(where)}]);\n`
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
// HTTP Basic is header-only by definition: the scheme is
|
|
220
|
+
// `Authorization: Basic base64(user:pass)`. It cannot be expressed as a
|
|
221
|
+
// query parameter or a cookie, so the branch is emitted only where it can
|
|
222
|
+
// mean something.
|
|
223
|
+
function basicActive(spec: AuthSpec): boolean {
|
|
224
|
+
return spec.basic && 'header' === spec.where
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
function basicBlock(spec: AuthSpec): string {
|
|
229
|
+
if (!basicActive(spec)) {
|
|
230
|
+
return ''
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return `
|
|
234
|
+
// True HTTP Basic Auth needs TWO credentials, base64-joined - a
|
|
235
|
+
// single token in the header (the branch below) can never
|
|
236
|
+
// authenticate against an API that actually checks
|
|
237
|
+
// \`Authorization: Basic base64(user:pass)\`.
|
|
238
|
+
if (true === (\\Voxgig\\Struct\\Struct::getpath($options, 'auth.basic') ?? false)) {
|
|
239
|
+
$secret = \\Voxgig\\Struct\\Struct::getprop($options, self::OPTION_SECRET, self::NOT_FOUND);
|
|
240
|
+
$apikey_val = is_string($apikey) && $apikey !== self::NOT_FOUND ? $apikey : '';
|
|
241
|
+
$secret_val = is_string($secret) && $secret !== self::NOT_FOUND ? $secret : '';
|
|
242
|
+
|
|
243
|
+
if ($apikey_val === '' || $secret_val === '') {
|
|
244
|
+
unset($headers[self::HEADER_AUTH]);
|
|
245
|
+
} else {
|
|
246
|
+
$auth_prefix = \\Voxgig\\Struct\\Struct::getpath($options, 'auth.prefix') ?? '';
|
|
247
|
+
$b64 = base64_encode("{$apikey_val}:{$secret_val}");
|
|
248
|
+
$headers[self::HEADER_AUTH] = $auth_prefix === ''
|
|
249
|
+
? $b64 : "{$auth_prefix} {$b64}";
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return [$spec, null];
|
|
253
|
+
}
|
|
254
|
+
`
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
function placeBlock(spec: AuthSpec): string {
|
|
259
|
+
const missing = `(is_string($apikey) && ($apikey === self::NOT_FOUND || $apikey === ''))
|
|
260
|
+
|| $apikey === null`
|
|
261
|
+
|
|
262
|
+
if ('cookie' === spec.where) {
|
|
263
|
+
// A MISSING CREDENTIAL CLEARS NOTHING HERE, for the reason `suppressed`
|
|
264
|
+
// gives: the cookie header is shared with the caller's own cookies.
|
|
265
|
+
return `
|
|
266
|
+
$missing = ${missing};
|
|
267
|
+
|
|
268
|
+
if (!$missing) {
|
|
269
|
+
// A COOKIE IS APPENDED, NEVER ASSIGNED: the header may already
|
|
270
|
+
// carry the caller's own cookies, and one \`Cookie:\` header
|
|
271
|
+
// holds all of them, separated by '; '.
|
|
272
|
+
$apikey_val = is_string($apikey) ? $apikey : '';
|
|
273
|
+
$existing = $headers[self::HEADER_COOKIE] ?? '';
|
|
274
|
+
$pair = self::COOKIE_AUTH . '=' . $apikey_val;
|
|
275
|
+
$headers[self::HEADER_COOKIE] = $existing === ''
|
|
276
|
+
? $pair : "{$existing}; {$pair}";
|
|
277
|
+
}
|
|
278
|
+
`
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if ('query' === spec.where) {
|
|
282
|
+
return `
|
|
283
|
+
if (
|
|
284
|
+
${missing}
|
|
285
|
+
) {
|
|
286
|
+
unset($query[self::QUERY_AUTH]);
|
|
287
|
+
} else {
|
|
288
|
+
// NO PREFIX IN A QUERY STRING. \`?token=Bearer%20abc\` is not a
|
|
289
|
+
// thing any API reads; the prefix is a header convention and is
|
|
290
|
+
// dropped here deliberately rather than silently concatenated.
|
|
291
|
+
$query[self::QUERY_AUTH] = is_string($apikey) ? $apikey : '';
|
|
292
|
+
}
|
|
293
|
+
`
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return `
|
|
297
|
+
if (
|
|
298
|
+
${missing}
|
|
299
|
+
) {
|
|
300
|
+
unset($headers[self::HEADER_AUTH]);
|
|
301
|
+
} else {
|
|
302
|
+
$auth_prefix = \\Voxgig\\Struct\\Struct::getpath($options, 'auth.prefix') ?? '';
|
|
303
|
+
$apikey_val = is_string($apikey) ? $apikey : '';
|
|
304
|
+
// Empty prefix (raw apiKey credential) must not add a leading space.
|
|
305
|
+
$headers[self::HEADER_AUTH] = $auth_prefix === ''
|
|
306
|
+
? $apikey_val : "{$auth_prefix} {$apikey_val}";
|
|
307
|
+
}
|
|
308
|
+
`
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
// A PHP single-quoted string literal: only the backslash and the quote
|
|
313
|
+
// itself need escaping, and single quotes never interpolate a `$name`.
|
|
314
|
+
function phpstr(s: string): string {
|
|
315
|
+
return "'" + String(s).replace(/\\/g, '\\\\').replace(/'/g, "\\'") + "'"
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
export {
|
|
320
|
+
PrepareAuth,
|
|
321
|
+
// Exported so the emitted PHP can be exercised directly (tests, and a
|
|
322
|
+
// one-line check of what a given placement renders) without standing up a
|
|
323
|
+
// full generation run.
|
|
324
|
+
render,
|
|
325
|
+
}
|
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
each,
|
|
14
14
|
isAuthActive,
|
|
15
15
|
isConfigData,
|
|
16
|
+
resolveAuthIn,
|
|
17
|
+
resolveAuthName,
|
|
16
18
|
resolveAuthPrefix,
|
|
17
19
|
serverVariables,
|
|
18
20
|
targetFeatures,
|
|
@@ -107,6 +109,13 @@ const Config = cmp(async function Config(props: any) {
|
|
|
107
109
|
const authActive = isAuthActive(model)
|
|
108
110
|
// config.auth.prefix override -> spec-derived info.security.prefix -> 'Bearer'
|
|
109
111
|
const authPrefix = resolveAuthPrefix(model)
|
|
112
|
+
// `in` and `name` travel with the prefix now. They were resolved by
|
|
113
|
+
// apidef all along and dropped here, so an apiKey-in-query API got an
|
|
114
|
+
// Authorization header it does not read. Emitted only when they differ
|
|
115
|
+
// from the defaults, so a header/Authorization SDK is byte-identical to
|
|
116
|
+
// what it generated before.
|
|
117
|
+
const authIn = resolveAuthIn(model)
|
|
118
|
+
const authName = resolveAuthName(model)
|
|
110
119
|
|
|
111
120
|
let baseUrl = ''
|
|
112
121
|
try { baseUrl = getModelPath(model, `main.${KIT}.info.servers.0.url`) } catch (_e) { }
|
|
@@ -121,7 +130,9 @@ const Config = cmp(async function Config(props: any) {
|
|
|
121
130
|
|
|
122
131
|
const authBlock = authActive
|
|
123
132
|
? ` "auth": {
|
|
124
|
-
"prefix": "${authPrefix}"
|
|
133
|
+
"prefix": "${authPrefix}",${'header' === authIn ? '' : `
|
|
134
|
+
"in": "${authIn}",`}${'Authorization' === authName ? '' : `
|
|
135
|
+
"name": "${authName}",`}
|
|
125
136
|
},\n`
|
|
126
137
|
: ''
|
|
127
138
|
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
|
|
25
25
|
import { Package } from './Package_py'
|
|
26
26
|
import { Config } from './Config_py'
|
|
27
|
+
import { PrepareAuth } from './PrepareAuth_py'
|
|
27
28
|
import { Gitignore } from './Gitignore_py'
|
|
28
29
|
import { MainEntity } from './MainEntity_py'
|
|
29
30
|
import { EntityTypes } from './EntityTypes_py'
|
|
@@ -190,6 +191,17 @@ if TYPE_CHECKING:
|
|
|
190
191
|
Config({ target })
|
|
191
192
|
})
|
|
192
193
|
|
|
194
|
+
// GENERATED, NOT COPIED. Where the credential goes is a fact about the
|
|
195
|
+
// API, and tm/ can only hold one answer. The blanket tm/py/pkg copy above
|
|
196
|
+
// no longer brings a prepare_auth.py in - it was deleted, since both
|
|
197
|
+
// writers would claim the same output path. See PrepareAuth_py.
|
|
198
|
+
//
|
|
199
|
+
// Called HERE, inside the <name>_sdk package folder and outside any
|
|
200
|
+
// other, because that is where the deleted template's own path
|
|
201
|
+
// (tm/py/pkg/utility/prepare_auth.py) put it: the component opens the one
|
|
202
|
+
// remaining segment, `utility`, itself.
|
|
203
|
+
PrepareAuth({ target })
|
|
204
|
+
|
|
193
205
|
// Generate feature factory module
|
|
194
206
|
File({ name: 'features.' + target.ext }, () => {
|
|
195
207
|
Content(`# ${model.const.Name} SDK feature factory
|