@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,387 @@
|
|
|
1
|
+
|
|
2
|
+
import {
|
|
3
|
+
Content,
|
|
4
|
+
File,
|
|
5
|
+
Folder,
|
|
6
|
+
cmp,
|
|
7
|
+
isAuthSuppressed,
|
|
8
|
+
isHttpBasicAuth,
|
|
9
|
+
resolveAuthIn,
|
|
10
|
+
resolveAuthName,
|
|
11
|
+
} from '@voxgig/sdkgen'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
perlStringLiteral,
|
|
16
|
+
} from './utility_perl'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
// WHERE THE CREDENTIAL GOES IS A FACT ABOUT THE API, so it is generated
|
|
20
|
+
// rather than templated. The perl peer of cmp/ts/PrepareAuth_ts.ts.
|
|
21
|
+
//
|
|
22
|
+
// This was a static file at `tm/perl/utility/prepare_auth.pm` that opened
|
|
23
|
+
// with `my $HEADER_AUTH = 'authorization';` and never looked further.
|
|
24
|
+
// apidef has always resolved the scheme's `in` and `name` into
|
|
25
|
+
// `main.kit.info.security` — joplin's says `in: "query", name: "token"` —
|
|
26
|
+
// and generation dropped both. The result was an SDK that sent a header
|
|
27
|
+
// the API does not read and never sent the query parameter it does, so it
|
|
28
|
+
// could not authenticate at all. Four repos in the cedar fleet shipped
|
|
29
|
+
// that way: joplin (`token`), pipedrive (`api_token`), trello (`key`),
|
|
30
|
+
// 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
|
|
35
|
+
// a bearer-token SDK, and no runtime `if` on a value that is fixed at
|
|
36
|
+
// generation time.
|
|
37
|
+
const PrepareAuth = cmp(async function PrepareAuth(props: any) {
|
|
38
|
+
const { model } = props.ctx$
|
|
39
|
+
|
|
40
|
+
// `!isAuthSuppressed`, NOT `isAuthActive`. The latter is also false when
|
|
41
|
+
// the SPEC merely declares no security scheme (`main.kit.info.auth:
|
|
42
|
+
// false`), and those SDKs still carry a credential: optspec always
|
|
43
|
+
// declares `apikey` and makeOptions fills `options.auth` from its
|
|
44
|
+
// defaults, so the runtime guard never fired and they have always sent
|
|
45
|
+
// it. Only an explicit `main.kit.config.auth.active: false` means "no
|
|
46
|
+
// credential, ever", which is what isAuthSuppressed reads.
|
|
47
|
+
const active = !isAuthSuppressed(model)
|
|
48
|
+
const where = resolveAuthIn(model)
|
|
49
|
+
const name = resolveAuthName(model)
|
|
50
|
+
const basic = isHttpBasicAuth(model)
|
|
51
|
+
|
|
52
|
+
// EXACTLY ONE folder, and it is opened HERE.
|
|
53
|
+
//
|
|
54
|
+
// Unlike ts — whose Main opens `Folder({name:'src'})` around Config,
|
|
55
|
+
// SdkError and this component — perl's Main calls PrepareAuth at the
|
|
56
|
+
// TOP LEVEL, with no folder open. The template's own path says what the
|
|
57
|
+
// layout is: `tm/perl/utility/prepare_auth.pm`, copied by Main's
|
|
58
|
+
// blanket `Copy({from:'tm/perl'})` (which excludes only `src/`), lands
|
|
59
|
+
// at `<sdk>/utility/prepare_auth.pm`. perl has no `src/` tree at all —
|
|
60
|
+
// Main writes `lib/`, `config.pm` and `features.pm` straight into the
|
|
61
|
+
// root. So this component must open `utility` and nothing above it:
|
|
62
|
+
// opening a second folder would write `src/utility/` or `./utility/`,
|
|
63
|
+
// a path nothing requires, while the copied file kept being loaded.
|
|
64
|
+
Folder({ name: 'utility' }, () => {
|
|
65
|
+
File({ name: 'prepare_auth.pm' }, () => {
|
|
66
|
+
Content(render({ Name: model.const.Name, active, where, name, basic }))
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
type AuthSpec = {
|
|
73
|
+
Name: string,
|
|
74
|
+
active: boolean,
|
|
75
|
+
where: string,
|
|
76
|
+
name: string,
|
|
77
|
+
basic: boolean,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
// PERL KEEPS ITS HEADER KEYS LOWERCASE, and here that is load-bearing
|
|
82
|
+
// rather than cosmetic. The template's constant was `'authorization'`,
|
|
83
|
+
// and two other generated perl files address that exact spelling:
|
|
84
|
+
// `feature/secrets_feature.pm` rewrites `$headers->{'authorization'}` at
|
|
85
|
+
// the transport seam (it rebuilds the header the way prepare_auth does,
|
|
86
|
+
// so the two must not drift), and `t/pipeline.t` asserts on
|
|
87
|
+
// `$ctx->{spec}{headers}{authorization}`. Emitting the resolved name
|
|
88
|
+
// verbatim would give every default SDK `Authorization` and silently
|
|
89
|
+
// split those three into two different hash keys.
|
|
90
|
+
//
|
|
91
|
+
// Header field names are case-insensitive on the wire (and HTTP/2
|
|
92
|
+
// requires them lowercase), so lowercasing costs nothing and keeps a
|
|
93
|
+
// header-based SDK behaving exactly as it did. A QUERY PARAMETER and a
|
|
94
|
+
// COOKIE name are case-sensitive, and go in verbatim.
|
|
95
|
+
function credName(spec: AuthSpec): string {
|
|
96
|
+
return 'header' === spec.where ? spec.name.toLowerCase() : spec.name
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
function render(spec: AuthSpec): string {
|
|
101
|
+
return spec.active ? renderActive(spec) : renderInactive(spec)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
// NO AUTH AT ALL. A public API's SDK gets a prepare_auth that is honest
|
|
106
|
+
// about it rather than one that deletes a header nobody set.
|
|
107
|
+
function renderInactive(spec: AuthSpec): string {
|
|
108
|
+
return `# ${spec.Name} SDK utility: prepare_auth
|
|
109
|
+
|
|
110
|
+
use strict;
|
|
111
|
+
use warnings;
|
|
112
|
+
|
|
113
|
+
use File::Basename ();
|
|
114
|
+
use Cwd ();
|
|
115
|
+
|
|
116
|
+
my $__dir;
|
|
117
|
+
BEGIN { $__dir = File::Basename::dirname(Cwd::abs_path(__FILE__)) }
|
|
118
|
+
require(Cwd::abs_path("$__dir/../lib/Voxgig/Struct.pm"));
|
|
119
|
+
require(Cwd::abs_path("$__dir/../core/helpers.pm"));
|
|
120
|
+
|
|
121
|
+
package ${spec.Name}Utilities;
|
|
122
|
+
|
|
123
|
+
our %REGISTRY;
|
|
124
|
+
|
|
125
|
+
# This API declares no authentication, so there is no credential to
|
|
126
|
+
# place. The entry stays in the registry because make_spec calls it
|
|
127
|
+
# unconditionally, and still guards the spec so the auth_no_spec contract
|
|
128
|
+
# holds for every SDK.
|
|
129
|
+
$REGISTRY{prepare_auth} = sub {
|
|
130
|
+
my ($ctx) = @_;
|
|
131
|
+
my $spec = $ctx->{spec};
|
|
132
|
+
return (undef, $ctx->make_error('auth_no_spec',
|
|
133
|
+
'Expected context spec property to be defined.')) unless $spec;
|
|
134
|
+
|
|
135
|
+
return ($spec, undef);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
1;
|
|
139
|
+
`
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
function renderActive(spec: AuthSpec): string {
|
|
144
|
+
const cred = perlStringLiteral(credName(spec))
|
|
145
|
+
|
|
146
|
+
// HTTP Basic is header-only by definition: the scheme is
|
|
147
|
+
// `Authorization: Basic base64(user:pass)`. It cannot be expressed as a
|
|
148
|
+
// query parameter or a cookie, so the branch — and the core module it
|
|
149
|
+
// needs — are emitted only where they can mean something.
|
|
150
|
+
const wantBasic = spec.basic && 'header' === spec.where
|
|
151
|
+
|
|
152
|
+
const use64 = wantBasic ? `use MIME::Base64 ();\n` : ''
|
|
153
|
+
const secretConst = wantBasic ?
|
|
154
|
+
`my $OPTION_SECRET = 'secret';\n` : ''
|
|
155
|
+
|
|
156
|
+
return `# ${spec.Name} SDK utility: prepare_auth
|
|
157
|
+
|
|
158
|
+
use strict;
|
|
159
|
+
use warnings;
|
|
160
|
+
|
|
161
|
+
use File::Basename ();
|
|
162
|
+
use Cwd ();
|
|
163
|
+
${use64}
|
|
164
|
+
my $__dir;
|
|
165
|
+
BEGIN { $__dir = File::Basename::dirname(Cwd::abs_path(__FILE__)) }
|
|
166
|
+
require(Cwd::abs_path("$__dir/../lib/Voxgig/Struct.pm"));
|
|
167
|
+
require(Cwd::abs_path("$__dir/../core/helpers.pm"));
|
|
168
|
+
|
|
169
|
+
package ${spec.Name}Utilities;
|
|
170
|
+
|
|
171
|
+
our %REGISTRY;
|
|
172
|
+
|
|
173
|
+
${placementNote(spec)}my $CRED_NAME = ${cred};
|
|
174
|
+
my $OPTION_APIKEY = 'apikey';
|
|
175
|
+
${secretConst}my $NOT_FOUND = '__NOTFOUND__';
|
|
176
|
+
|
|
177
|
+
$REGISTRY{prepare_auth} = sub {
|
|
178
|
+
my ($ctx) = @_;
|
|
179
|
+
my $spec = $ctx->{spec};
|
|
180
|
+
return (undef, $ctx->make_error('auth_no_spec',
|
|
181
|
+
'Expected context spec property to be defined.')) unless $spec;
|
|
182
|
+
|
|
183
|
+
${bag(spec)} my $options = $ctx->{client}->options_map;
|
|
184
|
+
|
|
185
|
+
# Public APIs that need no auth omit the options.auth block entirely.
|
|
186
|
+
${noCredNote(spec)} if (!defined ${spec.Name}Helpers::gp($options, 'auth')) {
|
|
187
|
+
${clear(spec, ' ')} return ($spec, undef);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
my $apikey = Voxgig::Struct::getprop($options, $OPTION_APIKEY, $NOT_FOUND);
|
|
191
|
+
${basicBlock(spec, wantBasic)}${credBlock(spec)}
|
|
192
|
+
return ($spec, undef);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
1;
|
|
196
|
+
`
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
// The missing-credential test, verbatim from the template so the four
|
|
201
|
+
// "no credential" shapes it distinguishes (undef, the NONE sentinel, a
|
|
202
|
+
// JSON null, the not-found marker or an empty string) keep behaving
|
|
203
|
+
// identically.
|
|
204
|
+
const MISSING = ` if (!defined $apikey || Voxgig::Struct::is_none($apikey)
|
|
205
|
+
|| Voxgig::Struct::is_jnull($apikey)
|
|
206
|
+
|| (!ref $apikey && ($apikey eq $NOT_FOUND || $apikey eq ''))) {`
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
// Place the credential, or clear what a previous option left behind.
|
|
210
|
+
//
|
|
211
|
+
// Header and query keep the template's if/else, because both have real
|
|
212
|
+
// work to do in both branches. COOKIE HAS NOTHING TO CLEAR (see clear()),
|
|
213
|
+
// and an `if` whose whole body is a comment is not perl anybody writes -
|
|
214
|
+
// so that one returns early and lets the placement run unindented.
|
|
215
|
+
function credBlock(spec: AuthSpec): string {
|
|
216
|
+
if ('cookie' === spec.where) {
|
|
217
|
+
return `
|
|
218
|
+
${MISSING}
|
|
219
|
+
return ($spec, undef);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
${place(spec).replace(/^ {4}/gm, ' ')}`
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return `
|
|
226
|
+
${MISSING}
|
|
227
|
+
${clear(spec, ' ')} }
|
|
228
|
+
else {
|
|
229
|
+
${place(spec)} }
|
|
230
|
+
`
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
// For the cookie placement, where the reason there is no `delete` has to
|
|
235
|
+
// be stated where the delete would have been.
|
|
236
|
+
function noCredNote(spec: AuthSpec): string {
|
|
237
|
+
if ('cookie' !== spec.where) {
|
|
238
|
+
return ''
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return ` # Nothing of OURS to remove either way: this SDK appends its pair to
|
|
242
|
+
# whatever cookie header the caller set and never stores one, and
|
|
243
|
+
# prepare_headers rebuilds that header from options on every request.
|
|
244
|
+
`
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
// A one-line reminder, in the generated file, of what the model said —
|
|
249
|
+
// so a reader of a joplin SDK is not left wondering why prepare_auth
|
|
250
|
+
// touches the query string.
|
|
251
|
+
function placementNote(spec: AuthSpec): string {
|
|
252
|
+
if ('query' === spec.where) {
|
|
253
|
+
return `# This API carries its credential as a QUERY PARAMETER (the spec's\n` +
|
|
254
|
+
`# securityScheme says in: query), not as a header.\n`
|
|
255
|
+
}
|
|
256
|
+
if ('cookie' === spec.where) {
|
|
257
|
+
return `# This API carries its credential as a COOKIE (the spec's\n` +
|
|
258
|
+
`# securityScheme says in: cookie), not as a header.\n`
|
|
259
|
+
}
|
|
260
|
+
return ''
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
// The bag the credential lands in. Cookies ride the header bag, because a
|
|
265
|
+
// cookie IS a header.
|
|
266
|
+
function bag(spec: AuthSpec): string {
|
|
267
|
+
if ('query' === spec.where) {
|
|
268
|
+
// make_spec fills spec.query from prepare_query BEFORE prepare_auth
|
|
269
|
+
// runs, so the bag is normally already there. A spec handed in
|
|
270
|
+
// without one still has to receive the credential, and assigning
|
|
271
|
+
// through an undef lexical would autovivify a hash the spec never
|
|
272
|
+
// sees - so the bag is created ON THE SPEC.
|
|
273
|
+
return ` my $query = $spec->{query};
|
|
274
|
+
unless (Voxgig::Struct::ismap($query)) {
|
|
275
|
+
$query = {};
|
|
276
|
+
$spec->{query} = $query;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
`
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// No blank line after it: this is the template's own line, in the
|
|
283
|
+
// template's own place, so a header-based SDK's prepare_auth.pm stays
|
|
284
|
+
// what it was.
|
|
285
|
+
return ` my $headers = $spec->{headers};
|
|
286
|
+
`
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
function clear(spec: AuthSpec, ind: string): string {
|
|
291
|
+
if ('query' === spec.where) {
|
|
292
|
+
return `${ind}delete $query->{$CRED_NAME};\n`
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if ('cookie' === spec.where) {
|
|
296
|
+
// NOTHING TO CLEAR. The header case deletes its header because the
|
|
297
|
+
// caller's own `options.headers` may carry a stale `authorization`
|
|
298
|
+
// that must not go out when auth is suppressed. A cookie is
|
|
299
|
+
// different: this SDK APPENDS its pair to whatever cookie header the
|
|
300
|
+
// caller set, and prepare_headers hands it a fresh clone of that
|
|
301
|
+
// header on every request - so there is never a pair of ours left
|
|
302
|
+
// behind, and surgically removing one pair from the caller's own
|
|
303
|
+
// cookie header would be deleting something they put there
|
|
304
|
+
// deliberately. noCredNote() says so in the generated file.
|
|
305
|
+
return ''
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return `${ind}delete $headers->{$CRED_NAME};\n`
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
function place(spec: AuthSpec): string {
|
|
313
|
+
if ('query' === spec.where) {
|
|
314
|
+
// NO PREFIX IN A QUERY STRING. `?token=Bearer%20abc` is not a thing
|
|
315
|
+
// any API reads; the prefix is a header convention and is dropped
|
|
316
|
+
// here deliberately rather than silently concatenated.
|
|
317
|
+
return ` # NO PREFIX IN A QUERY STRING: \`?token=Bearer%20abc\` is not a thing
|
|
318
|
+
# any API reads. The prefix is a header convention, and is dropped
|
|
319
|
+
# here deliberately rather than silently concatenated. make_url
|
|
320
|
+
# url-encodes both the name and the value.
|
|
321
|
+
$query->{$CRED_NAME} = (!ref $apikey) ? "$apikey" : '';
|
|
322
|
+
`
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if ('cookie' === spec.where) {
|
|
326
|
+
return ` my $apikey_val = (!ref $apikey) ? "$apikey" : '';
|
|
327
|
+
my $pair = "$CRED_NAME=$apikey_val";
|
|
328
|
+
# APPEND, never clobber: the caller's own cookie header may already
|
|
329
|
+
# carry a session or consent pair that the API needs alongside this
|
|
330
|
+
# credential.
|
|
331
|
+
my $cookie = $headers->{'cookie'};
|
|
332
|
+
$cookie = '' unless defined $cookie && !ref $cookie;
|
|
333
|
+
$headers->{'cookie'} = ('' eq $cookie) ? $pair : "$cookie; $pair";
|
|
334
|
+
`
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return ` my $auth_prefix = ${spec.Name}Helpers::gpath($options, 'auth.prefix');
|
|
338
|
+
$auth_prefix = '' unless defined $auth_prefix && !ref $auth_prefix;
|
|
339
|
+
my $apikey_val = (!ref $apikey) ? "$apikey" : '';
|
|
340
|
+
# Empty prefix (raw apiKey credential) must not add a leading space.
|
|
341
|
+
$headers->{$CRED_NAME} =
|
|
342
|
+
('' eq $auth_prefix) ? $apikey_val : "$auth_prefix $apikey_val";
|
|
343
|
+
`
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
function basicBlock(spec: AuthSpec, wantBasic: boolean): string {
|
|
348
|
+
if (!wantBasic) {
|
|
349
|
+
return ''
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return `
|
|
353
|
+
# True HTTP Basic Auth needs TWO credentials, base64-joined - a single
|
|
354
|
+
# token in the header (the branch below) can never authenticate against
|
|
355
|
+
# an API that actually checks \`Authorization: Basic base64(user:pass)\`.
|
|
356
|
+
if (${spec.Name}Helpers::is_true(${spec.Name}Helpers::gpath($options, 'auth.basic'))) {
|
|
357
|
+
my $secret = Voxgig::Struct::getprop($options, $OPTION_SECRET, $NOT_FOUND);
|
|
358
|
+
|
|
359
|
+
my $no_apikey = !defined $apikey || Voxgig::Struct::is_none($apikey)
|
|
360
|
+
|| Voxgig::Struct::is_jnull($apikey)
|
|
361
|
+
|| (!ref $apikey && ($apikey eq $NOT_FOUND || $apikey eq ''));
|
|
362
|
+
my $no_secret = !defined $secret || Voxgig::Struct::is_none($secret)
|
|
363
|
+
|| Voxgig::Struct::is_jnull($secret)
|
|
364
|
+
|| (!ref $secret && ($secret eq $NOT_FOUND || $secret eq ''));
|
|
365
|
+
|
|
366
|
+
if ($no_apikey || $no_secret) {
|
|
367
|
+
delete $headers->{$CRED_NAME};
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
my $auth_prefix = ${spec.Name}Helpers::gpath($options, 'auth.prefix');
|
|
371
|
+
$auth_prefix = '' unless defined $auth_prefix && !ref $auth_prefix;
|
|
372
|
+
# '' as the eol: encode_base64 wraps at 76 columns by default, and a
|
|
373
|
+
# newline inside a header value is not a header value.
|
|
374
|
+
my $b64 = MIME::Base64::encode_base64("$apikey:$secret", '');
|
|
375
|
+
$headers->{$CRED_NAME} =
|
|
376
|
+
('' eq $auth_prefix) ? $b64 : "$auth_prefix $b64";
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return ($spec, undef);
|
|
380
|
+
}
|
|
381
|
+
`
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
export {
|
|
386
|
+
PrepareAuth
|
|
387
|
+
}
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
isAuthActive,
|
|
15
15
|
isConfigData,
|
|
16
16
|
rawStringLiteral,
|
|
17
|
+
resolveAuthIn,
|
|
18
|
+
resolveAuthName,
|
|
17
19
|
resolveAuthPrefix,
|
|
18
20
|
serverVariables,
|
|
19
21
|
targetFeatures,
|
|
@@ -121,6 +123,13 @@ const Config = cmp(async function Config(props: any) {
|
|
|
121
123
|
const authActive = isAuthActive(model)
|
|
122
124
|
// config.auth.prefix override -> spec-derived info.security.prefix -> 'Bearer'
|
|
123
125
|
const authPrefix = resolveAuthPrefix(model)
|
|
126
|
+
// `in` and `name` travel with the prefix now. They were resolved by
|
|
127
|
+
// apidef all along and dropped here, so an apiKey-in-query API got an
|
|
128
|
+
// Authorization header it does not read. Emitted only when they differ
|
|
129
|
+
// from the defaults, so a header/Authorization SDK is byte-identical to
|
|
130
|
+
// what it generated before.
|
|
131
|
+
const authIn = resolveAuthIn(model)
|
|
132
|
+
const authName = resolveAuthName(model)
|
|
124
133
|
|
|
125
134
|
let baseUrl = ''
|
|
126
135
|
try { baseUrl = getModelPath(model, `main.${KIT}.info.servers.0.url`) } catch (_e) { }
|
|
@@ -137,7 +146,9 @@ const Config = cmp(async function Config(props: any) {
|
|
|
137
146
|
|
|
138
147
|
const authBlock = authActive
|
|
139
148
|
? ` "auth" => [
|
|
140
|
-
"prefix" => "${authPrefix}"
|
|
149
|
+
"prefix" => "${authPrefix}",${'header' === authIn ? '' : `
|
|
150
|
+
"in" => ${phps(authIn)},`}${'Authorization' === authName ? '' : `
|
|
151
|
+
"name" => ${phps(authName)},`}
|
|
141
152
|
],\n`
|
|
142
153
|
: ''
|
|
143
154
|
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
|
|
24
24
|
import { Package } from './Package_php'
|
|
25
25
|
import { Config } from './Config_php'
|
|
26
|
+
import { PrepareAuth } from './PrepareAuth_php'
|
|
26
27
|
import { Gitignore } from './Gitignore_php'
|
|
27
28
|
import { MainEntity } from './MainEntity_php'
|
|
28
29
|
import { EntityTypes } from './EntityTypes_php'
|
|
@@ -105,6 +106,12 @@ const Main = cmp(async function Main(props: any) {
|
|
|
105
106
|
Config({ target })
|
|
106
107
|
})
|
|
107
108
|
|
|
109
|
+
// GENERATED, NOT COPIED. Where the credential goes is a fact about the
|
|
110
|
+
// API, and tm/ can only hold one answer. The component opens `utility/`
|
|
111
|
+
// itself, because nothing is open here: Main writes into the target root.
|
|
112
|
+
// See PrepareAuth_php.
|
|
113
|
+
PrepareAuth({ target })
|
|
114
|
+
|
|
108
115
|
// Generate typed models (types/<Sdk>Types.php) — classmap-autoloaded.
|
|
109
116
|
EntityTypes({ target })
|
|
110
117
|
|