@voxgig/sdkgen 1.3.18 → 1.4.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/dist/cmp/Deploy.js +16 -16
- package/dist/cmp/Deploy.js.map +1 -1
- package/dist/cmp/ReadmeTop.js +13 -11
- package/dist/cmp/ReadmeTop.js.map +1 -1
- package/dist/helpers/naming.d.ts +3 -1
- package/dist/helpers/naming.js +65 -0
- package/dist/helpers/naming.js.map +1 -1
- package/dist/helpers/serverVars.d.ts +10 -0
- package/dist/helpers/serverVars.js +87 -0
- package/dist/helpers/serverVars.js.map +1 -0
- package/dist/sdkgen.d.ts +3 -2
- package/dist/sdkgen.js +6 -1
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/project/.sdk/model/target/go-cli.aontu +2 -2
- package/project/.sdk/src/cmp/csharp/TestEntity_csharp.ts +22 -15
- package/project/.sdk/src/cmp/go/Config_go.ts +10 -1
- package/project/.sdk/src/cmp/go-cli/Main_go-cli.ts +54 -35
- package/project/.sdk/src/cmp/go-cli/fragment/main.fragment.go +7 -7
- package/project/.sdk/src/cmp/go-cli/fragment/words.fragment.go +21 -18
- package/project/.sdk/src/cmp/js/Config_js.ts +11 -0
- package/project/.sdk/src/cmp/js/fragment/Config.fragment.js +2 -2
- package/project/.sdk/src/cmp/js/fragment/Direct.test.fragment.js +4 -0
- package/project/.sdk/src/cmp/lua/Config_lua.ts +10 -1
- package/project/.sdk/src/cmp/php/Config_php.ts +13 -2
- package/project/.sdk/src/cmp/py/Config_py.ts +10 -1
- package/project/.sdk/src/cmp/rb/Config_rb.ts +12 -1
- package/project/.sdk/src/cmp/rust/TestEntity_rust.ts +25 -18
- package/project/.sdk/src/cmp/swift/EntityTypes_swift.ts +10 -3
- package/project/.sdk/src/cmp/ts/Config_ts.ts +11 -0
- package/project/.sdk/src/cmp/ts/fragment/Config.fragment.ts +2 -2
- package/project/.sdk/src/cmp/ts/fragment/Direct.test.fragment.ts +4 -0
- package/project/.sdk/tm/c/utility/prepare_method.c +18 -0
- package/project/.sdk/tm/clojure/src/sdk/core.clj +9 -1
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +13 -0
- package/project/.sdk/tm/csharp/Makefile +6 -6
- package/project/.sdk/tm/csharp/utility/PrepareMethod.cs +11 -0
- package/project/.sdk/tm/dart/Makefile +3 -3
- package/project/.sdk/tm/dart/lib/utility/PrepareMethodUtility.dart +10 -0
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +8 -1
- package/project/.sdk/tm/go/Makefile +6 -6
- package/project/.sdk/tm/go/test/primary_utility_test.go +5 -0
- package/project/.sdk/tm/go/utility/make_options.go +48 -0
- package/project/.sdk/tm/go/utility/prepare_method.go +14 -1
- package/project/.sdk/tm/haskell/src/SdkRuntime.hs +13 -4
- package/project/.sdk/tm/java/Makefile +6 -6
- package/project/.sdk/tm/java/utility/PrepareMethod.java +9 -0
- package/project/.sdk/tm/js/Makefile +6 -6
- package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +39 -1
- package/project/.sdk/tm/js/src/utility/PrepareMethodUtility.js +9 -0
- package/project/.sdk/tm/js/test/utility/PrimaryUtility.test.js +5 -0
- package/project/.sdk/tm/kotlin/Makefile +6 -6
- package/project/.sdk/tm/kotlin/utility/Prepare.kt +9 -0
- package/project/.sdk/tm/lean/src/SdkUtility.lean +6 -1
- package/project/.sdk/tm/lua/Makefile +7 -7
- package/project/.sdk/tm/lua/feature/test_feature.lua +29 -1
- package/project/.sdk/tm/lua/test/primary_utility_test.lua +4 -0
- package/project/.sdk/tm/lua/utility/make_options.lua +39 -0
- package/project/.sdk/tm/lua/utility/prepare_method.lua +11 -0
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +9 -3
- package/project/.sdk/tm/perl/Makefile +6 -6
- package/project/.sdk/tm/perl/utility/prepare_method.pm +17 -0
- package/project/.sdk/tm/php/Makefile +6 -6
- package/project/.sdk/tm/php/feature/TestFeature.php +30 -2
- package/project/.sdk/tm/php/test/PrimaryUtilityTest.php +5 -0
- package/project/.sdk/tm/php/utility/MakeOptions.php +44 -0
- package/project/.sdk/tm/php/utility/PrepareMethod.php +11 -0
- package/project/.sdk/tm/py/Makefile +6 -6
- package/project/.sdk/tm/py/test/test_primary_utility.py +5 -0
- package/project/.sdk/tm/py/utility/make_options.py +42 -0
- package/project/.sdk/tm/py/utility/prepare_method.py +10 -0
- package/project/.sdk/tm/rb/Makefile +6 -6
- package/project/.sdk/tm/rb/test/primary_utility_test.rb +4 -0
- package/project/.sdk/tm/rb/utility/make_options.rb +37 -0
- package/project/.sdk/tm/rb/utility/prepare_method.rb +10 -1
- package/project/.sdk/tm/rust/Makefile +6 -6
- package/project/.sdk/tm/rust/utility/prepare_method.rs +12 -0
- package/project/.sdk/tm/scala/utility/Prepare.scala +14 -8
- package/project/.sdk/tm/swift/Makefile +6 -6
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Prepare.swift +8 -0
- package/project/.sdk/tm/ts/Makefile +6 -6
- package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +39 -1
- package/project/.sdk/tm/ts/src/utility/PrepareMethodUtility.ts +9 -0
- package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +4 -0
- package/project/.sdk/tm/zig/core/utility.zig +15 -0
- package/src/cmp/Deploy.ts +16 -16
- package/src/cmp/ReadmeTop.ts +13 -11
- package/src/helpers/naming.ts +71 -0
- package/src/helpers/serverVars.ts +105 -0
- package/src/sdkgen.ts +6 -1
|
@@ -19,7 +19,7 @@ class Config {
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
main = {
|
|
22
|
-
name: '
|
|
22
|
+
name: '$$const.Name$$',
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
|
|
@@ -31,7 +31,7 @@ class Config {
|
|
|
31
31
|
options = {
|
|
32
32
|
base: '$$main.kit.info.servers.0.url$$',
|
|
33
33
|
|
|
34
|
-
'AUTHBLOCK'headers: 'HEADERS',
|
|
34
|
+
'SERVERBLOCK''AUTHBLOCK'headers: 'HEADERS',
|
|
35
35
|
|
|
36
36
|
entity: {
|
|
37
37
|
// #EntityConfigs
|
|
@@ -24,6 +24,10 @@ describe('EntityNameDirect', async () => {
|
|
|
24
24
|
|
|
25
25
|
test('direct-exists', async () => {
|
|
26
26
|
const sdk = new ProjectNameSDK({
|
|
27
|
+
// Concrete base: a live construction must satisfy any server
|
|
28
|
+
// variables a templated base URL declares; overriding base with a
|
|
29
|
+
// literal (as the direct flow tests do) sidesteps the requirement.
|
|
30
|
+
base: 'http://localhost:8080',
|
|
27
31
|
system: { fetch: async () => ({}) }
|
|
28
32
|
})
|
|
29
33
|
assert('function' === typeof sdk.direct)
|
|
@@ -2,10 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
#include "sdk.h"
|
|
4
4
|
|
|
5
|
+
#include <ctype.h>
|
|
5
6
|
#include <string.h>
|
|
6
7
|
|
|
7
8
|
const char* prepare_method_util(Context* ctx) {
|
|
8
9
|
const char* op = ctx->op->name;
|
|
10
|
+
|
|
11
|
+
// The API definition is authoritative: a POST-only or PATCH-based API
|
|
12
|
+
// exposes `update` as POST or PATCH, not the PUT the op name implies.
|
|
13
|
+
// Only fall back to the op-name convention when the point has no method.
|
|
14
|
+
// Uppercased into a static buffer so the returned pointer stays valid for
|
|
15
|
+
// the caller's lifetime, matching the static-string contract above.
|
|
16
|
+
const char* m = get_str(ctx->point, "method");
|
|
17
|
+
if (m && '\0' != m[0]) {
|
|
18
|
+
static char upper[16];
|
|
19
|
+
size_t i = 0;
|
|
20
|
+
for (; i < sizeof(upper) - 1 && '\0' != m[i]; i++) {
|
|
21
|
+
upper[i] = (char)toupper((unsigned char)m[i]);
|
|
22
|
+
}
|
|
23
|
+
upper[i] = '\0';
|
|
24
|
+
return upper;
|
|
25
|
+
}
|
|
26
|
+
|
|
9
27
|
if (strcmp(op, "create") == 0) return "POST";
|
|
10
28
|
if (strcmp(op, "update") == 0) return "PUT";
|
|
11
29
|
if (strcmp(op, "load") == 0) return "GET";
|
|
@@ -389,7 +389,15 @@
|
|
|
389
389
|
|
|
390
390
|
(defn u-clean [_ctx val] val)
|
|
391
391
|
|
|
392
|
-
|
|
392
|
+
;; The API definition is authoritative: a POST-only or PATCH-based API
|
|
393
|
+
;; exposes `update` as POST or PATCH, not the PUT the op name implies.
|
|
394
|
+
;; Only fall back to the op-name convention when the point has no method.
|
|
395
|
+
(defn u-prepare-method [ctx]
|
|
396
|
+
(let [point (oget ctx :point)
|
|
397
|
+
m (when point (vs/getprop point "method"))]
|
|
398
|
+
(if (and (string? m) (not= "" m))
|
|
399
|
+
(str/upper-case m)
|
|
400
|
+
(get METHOD-MAP (op-name (oget ctx :op)) "GET"))))
|
|
393
401
|
|
|
394
402
|
(defn u-prepare-headers [ctx]
|
|
395
403
|
(let [options (client-options-map (oget ctx :client))
|
|
@@ -654,6 +654,19 @@ inline ResultPtr makeResult(CtxPtr ctx) {
|
|
|
654
654
|
|
|
655
655
|
inline std::string prepareMethod(CtxPtr ctx) {
|
|
656
656
|
const std::string& opname = ctx->op->name;
|
|
657
|
+
|
|
658
|
+
// The API definition is authoritative: a POST-only or PATCH-based API
|
|
659
|
+
// exposes `update` as POST or PATCH, not the PUT the op name implies.
|
|
660
|
+
// Only fall back to the op-name convention when the point has no method.
|
|
661
|
+
Value pm = getp(ctx->point, "method");
|
|
662
|
+
if (pm.is_string()) {
|
|
663
|
+
std::string m = as_str(pm);
|
|
664
|
+
if (!m.empty()) {
|
|
665
|
+
for (auto& c : m) c = (char)std::toupper((unsigned char)c);
|
|
666
|
+
return m;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
657
670
|
if (opname == "create") return "POST";
|
|
658
671
|
if (opname == "update") return "PUT";
|
|
659
672
|
if (opname == "load") return "GET";
|
|
@@ -15,21 +15,21 @@ clean:
|
|
|
15
15
|
# publishing a version currently means pushing the tag csharp/vX.Y.Z. When the
|
|
16
16
|
# registry goes live, `dotnet pack` + `dotnet nuget push` slot in before the
|
|
17
17
|
# tag (mirroring the voxgig/struct csharp Makefile). Run headless under the
|
|
18
|
-
#
|
|
18
|
+
# boru key vault, which injects the GitHub token so nothing lands on disk or
|
|
19
19
|
# argv:
|
|
20
|
-
#
|
|
21
|
-
#
|
|
20
|
+
# boru vault exec --for=github=<alias> -- make publish
|
|
21
|
+
# boru `--dry-run` injects a filler token we detect to rehearse (no tag/push).
|
|
22
22
|
# set -e + tag-last: a failed push never leaves a tag behind.
|
|
23
23
|
VERSION := $(shell cat VERSION)
|
|
24
24
|
TAG := csharp/v$(VERSION)
|
|
25
|
-
|
|
25
|
+
BORU_DRY_RUN_FILLER := BORU-DRY-RUN-FILLER-NOT-A-REAL-SECRET
|
|
26
26
|
publish: test
|
|
27
27
|
@if git rev-parse -q --verify "refs/tags/$(TAG)" >/dev/null; then \
|
|
28
28
|
echo "tag $(TAG) already exists — bump VERSION first"; exit 1; fi
|
|
29
29
|
@set -e; \
|
|
30
30
|
token="$${GITHUB_TOKEN:-$$GH_TOKEN}"; \
|
|
31
|
-
if [ "$$token" = "$(
|
|
32
|
-
echo "[dry-run]
|
|
31
|
+
if [ "$$token" = "$(BORU_DRY_RUN_FILLER)" ]; then \
|
|
32
|
+
echo "[dry-run] boru filler token detected: would tag $(TAG); nothing pushed."; \
|
|
33
33
|
exit 0; \
|
|
34
34
|
fi; \
|
|
35
35
|
git tag -a "$(TAG)" -m "csharp v$(VERSION)"; \
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// ProjectName SDK utility: prepareMethod.
|
|
2
2
|
|
|
3
|
+
using Voxgig.Struct;
|
|
4
|
+
|
|
3
5
|
namespace ProjectNameSdk.Util;
|
|
4
6
|
|
|
5
7
|
public static partial class SdkUtility
|
|
@@ -17,6 +19,15 @@ public static partial class SdkUtility
|
|
|
17
19
|
internal static string PrepareMethodUtil(Context ctx)
|
|
18
20
|
{
|
|
19
21
|
var opname = ctx.Op!.Name;
|
|
22
|
+
|
|
23
|
+
// The API definition is authoritative: a POST-only or PATCH-based API
|
|
24
|
+
// exposes `update` as POST or PATCH, not the PUT the op name implies.
|
|
25
|
+
// Only fall back to the op-name convention when the point has no method.
|
|
26
|
+
if (StructUtils.GetProp(ctx.Point, "method") is string pm && "" != pm)
|
|
27
|
+
{
|
|
28
|
+
return pm.ToUpperInvariant();
|
|
29
|
+
}
|
|
30
|
+
|
|
20
31
|
return MethodMap.TryGetValue(opname, out var m) ? m : "GET";
|
|
21
32
|
}
|
|
22
33
|
}
|
|
@@ -22,14 +22,14 @@ clean:
|
|
|
22
22
|
# the tag. set -e + tag-last: a failed upload never leaves a tag behind.
|
|
23
23
|
VERSION := $(shell sed -nE 's/^version: *([0-9][^ ]*).*/\1/p' pubspec.yaml | head -1)
|
|
24
24
|
TAG := dart/v$(VERSION)
|
|
25
|
-
|
|
25
|
+
BORU_DRY_RUN_FILLER := BORU-DRY-RUN-FILLER-NOT-A-REAL-SECRET
|
|
26
26
|
publish: test
|
|
27
27
|
@if git rev-parse -q --verify "refs/tags/$(TAG)" >/dev/null; then \
|
|
28
28
|
echo "tag $(TAG) already exists — bump pubspec.yaml version first"; exit 1; fi
|
|
29
29
|
@set -e; \
|
|
30
30
|
token="$${GITHUB_TOKEN:-$$GH_TOKEN}"; \
|
|
31
|
-
if [ "$$token" = "$(
|
|
32
|
-
echo "[dry-run]
|
|
31
|
+
if [ "$$token" = "$(BORU_DRY_RUN_FILLER)" ]; then \
|
|
32
|
+
echo "[dry-run] boru filler token detected: would publish to pub.dev and tag $(TAG); nothing pushed."; \
|
|
33
33
|
exit 0; \
|
|
34
34
|
fi; \
|
|
35
35
|
if [ -n "$$PUB_CREDENTIALS" ]; then \
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import 'voxgig_struct.dart' as vs;
|
|
2
|
+
|
|
1
3
|
dynamic prepareMethod(dynamic ctx) {
|
|
2
4
|
final op = ctx.op;
|
|
3
5
|
final opname = op.name;
|
|
@@ -11,5 +13,13 @@ dynamic prepareMethod(dynamic ctx) {
|
|
|
11
13
|
'patch': 'PATCH',
|
|
12
14
|
};
|
|
13
15
|
|
|
16
|
+
// The API definition is authoritative: a POST-only or PATCH-based API
|
|
17
|
+
// exposes `update` as POST or PATCH, not the PUT the op name implies.
|
|
18
|
+
// Only fall back to the op-name convention when the point has no method.
|
|
19
|
+
final method = vs.getprop(ctx.point, 'method');
|
|
20
|
+
if (method is String && '' != method) {
|
|
21
|
+
return method.toUpperCase();
|
|
22
|
+
}
|
|
23
|
+
|
|
14
24
|
return methodMap[opname];
|
|
15
25
|
}
|
|
@@ -888,7 +888,14 @@ defmodule ProjectName.Utility do
|
|
|
888
888
|
|
|
889
889
|
def prepare_method_impl(ctx) do
|
|
890
890
|
opname = S.getprop(S.getprop(ctx, "op"), "name")
|
|
891
|
-
|
|
891
|
+
|
|
892
|
+
# The API definition is authoritative: a POST-only or PATCH-based API
|
|
893
|
+
# exposes `update` as POST or PATCH, not the PUT the op name implies.
|
|
894
|
+
# Only fall back to the op-name convention when the point has no method.
|
|
895
|
+
case S.getprop(S.getprop(ctx, "point"), "method") do
|
|
896
|
+
m when is_binary(m) and m != "" -> String.upcase(m)
|
|
897
|
+
_ -> Map.get(@method_map, opname, "GET")
|
|
898
|
+
end
|
|
892
899
|
end
|
|
893
900
|
|
|
894
901
|
def prepare_headers_impl(ctx) do
|
|
@@ -13,10 +13,10 @@ clean:
|
|
|
13
13
|
# Go modules have no registry upload — publishing a version means pushing the
|
|
14
14
|
# tag go/vX.Y.Z (this module is the go/ subdir, and Go's submodule-tag
|
|
15
15
|
# convention is <subdir>/vX.Y.Z), which the module proxy + pkg.go.dev pick up on
|
|
16
|
-
# first fetch. Run headless under the
|
|
16
|
+
# first fetch. Run headless under the boru key vault, which injects the GitHub
|
|
17
17
|
# token so nothing lands on disk or argv:
|
|
18
|
-
#
|
|
19
|
-
#
|
|
18
|
+
# boru vault exec --for=github=<alias> -- make publish
|
|
19
|
+
# boru `--dry-run` injects a filler token we detect to rehearse (no tag/push).
|
|
20
20
|
# set -e + tag-last: a failed push never leaves a tag behind.
|
|
21
21
|
#
|
|
22
22
|
# NOTE: publishing the git tag is deliberately NOT gated on `go test` (every
|
|
@@ -24,14 +24,14 @@ clean:
|
|
|
24
24
|
# block the module tag). Run `make test` separately in CI to gate quality.
|
|
25
25
|
VERSION := $(shell cat VERSION)
|
|
26
26
|
TAG := go/v$(VERSION)
|
|
27
|
-
|
|
27
|
+
BORU_DRY_RUN_FILLER := BORU-DRY-RUN-FILLER-NOT-A-REAL-SECRET
|
|
28
28
|
publish:
|
|
29
29
|
@if git rev-parse -q --verify "refs/tags/$(TAG)" >/dev/null; then \
|
|
30
30
|
echo "tag $(TAG) already exists — bump VERSION first"; exit 1; fi
|
|
31
31
|
@set -e; \
|
|
32
32
|
token="$${GITHUB_TOKEN:-$$GH_TOKEN}"; \
|
|
33
|
-
if [ "$$token" = "$(
|
|
34
|
-
echo "[dry-run]
|
|
33
|
+
if [ "$$token" = "$(BORU_DRY_RUN_FILLER)" ]; then \
|
|
34
|
+
echo "[dry-run] boru filler token detected: would tag $(TAG) for pkg.go.dev; nothing pushed."; \
|
|
35
35
|
exit 0; \
|
|
36
36
|
fi; \
|
|
37
37
|
git tag -a "$(TAG)" -m "go v$(VERSION)"; \
|
|
@@ -252,6 +252,10 @@ func TestPrimaryUtility(t *testing.T) {
|
|
|
252
252
|
t.Run("fetcher-live", func(t *testing.T) {
|
|
253
253
|
calls := []map[string]any{}
|
|
254
254
|
liveClient := sdk.NewProjectNameSDK(map[string]any{
|
|
255
|
+
// Concrete base: a live construction must satisfy any server
|
|
256
|
+
// variables a templated base URL declares; a literal base
|
|
257
|
+
// sidesteps the requirement.
|
|
258
|
+
"base": "http://localhost:8080",
|
|
255
259
|
"system": map[string]any{
|
|
256
260
|
"fetch": func(url string, fetchdef map[string]any) (map[string]any, error) {
|
|
257
261
|
calls = append(calls, map[string]any{"url": url, "init": fetchdef})
|
|
@@ -282,6 +286,7 @@ func TestPrimaryUtility(t *testing.T) {
|
|
|
282
286
|
t.Run("fetcher-blocked-test-mode", func(t *testing.T) {
|
|
283
287
|
// Create a live SDK then set mode to test (not using TestSDK, which installs test feature)
|
|
284
288
|
blockedClient := sdk.NewProjectNameSDK(map[string]any{
|
|
289
|
+
"base": "http://localhost:8080",
|
|
285
290
|
"system": map[string]any{
|
|
286
291
|
"fetch": func(url string, fetchdef map[string]any) (map[string]any, error) {
|
|
287
292
|
return map[string]any{}, nil
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package utility
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"regexp"
|
|
4
5
|
"sort"
|
|
5
6
|
"strings"
|
|
6
7
|
|
|
@@ -9,6 +10,9 @@ import (
|
|
|
9
10
|
"GOMODULE/core"
|
|
10
11
|
)
|
|
11
12
|
|
|
13
|
+
// {name} placeholders in a templated server URL (OpenAPI server variables).
|
|
14
|
+
var serverVarRe = regexp.MustCompile(`\{[A-Za-z0-9_]+\}`)
|
|
15
|
+
|
|
12
16
|
func makeOptionsUtil(ctx *core.Context) map[string]any {
|
|
13
17
|
options := ctx.Options
|
|
14
18
|
if options == nil {
|
|
@@ -108,6 +112,13 @@ func makeOptionsUtil(ctx *core.Context) map[string]any {
|
|
|
108
112
|
"clean": map[string]any{
|
|
109
113
|
"keys": "key,token,id",
|
|
110
114
|
},
|
|
115
|
+
// Server-variable values for a templated base URL (OpenAPI server
|
|
116
|
+
// variables): {name} placeholders in "base" are substituted from this
|
|
117
|
+
// map at construction. Spec defaults arrive via the generated config;
|
|
118
|
+
// user values override them.
|
|
119
|
+
"server": map[string]any{
|
|
120
|
+
"`$CHILD`": "",
|
|
121
|
+
},
|
|
111
122
|
}
|
|
112
123
|
|
|
113
124
|
// Preserve system.fetch before merge/validate.
|
|
@@ -120,6 +131,43 @@ func makeOptionsUtil(ctx *core.Context) map[string]any {
|
|
|
120
131
|
validated, _ := vs.Validate(merged, optspec)
|
|
121
132
|
opts = validated.(map[string]any)
|
|
122
133
|
|
|
134
|
+
// Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
|
|
135
|
+
// Every placeholder must resolve to a non-empty value: from
|
|
136
|
+
// options["server"] (user), else the config default. A placeholder that
|
|
137
|
+
// resolves to "" is a construction error in live mode — the URL cannot
|
|
138
|
+
// work — but in test mode substitutes the deterministic value
|
|
139
|
+
// "test-<name>" so offline tests need no configuration. The SDK
|
|
140
|
+
// constructor has no error return, so a missing required variable
|
|
141
|
+
// PANICS (construction-time misconfiguration, as regexp.MustCompile).
|
|
142
|
+
if base, ok := opts["base"].(string); ok && strings.Contains(base, "{") {
|
|
143
|
+
testmode := false
|
|
144
|
+
if ta, ok := vs.GetPath([]any{"test", "active"}, opts).(bool); ok && ta {
|
|
145
|
+
testmode = true
|
|
146
|
+
}
|
|
147
|
+
if fa, ok := vs.GetPath([]any{"feature", "test", "active"}, opts).(bool); ok && fa {
|
|
148
|
+
testmode = true
|
|
149
|
+
}
|
|
150
|
+
server := core.ToMapAny(opts["server"])
|
|
151
|
+
sdkname := "SDK"
|
|
152
|
+
if mn, ok := vs.GetPath([]any{"main", "name"}, config).(string); ok && mn != "" {
|
|
153
|
+
sdkname = mn
|
|
154
|
+
}
|
|
155
|
+
resolved := serverVarRe.ReplaceAllStringFunc(base, func(ph string) string {
|
|
156
|
+
name := ph[1 : len(ph)-1]
|
|
157
|
+
val, _ := server[name].(string)
|
|
158
|
+
if val == "" {
|
|
159
|
+
if testmode {
|
|
160
|
+
return "test-" + name
|
|
161
|
+
}
|
|
162
|
+
panic(sdkname + ": the server variable '" + name + "' is required: " +
|
|
163
|
+
"the API base URL is '" + base + "' — pass " +
|
|
164
|
+
`options["server"].(map)["` + name + `"] in the SDK options`)
|
|
165
|
+
}
|
|
166
|
+
return val
|
|
167
|
+
})
|
|
168
|
+
opts["base"] = resolved
|
|
169
|
+
}
|
|
170
|
+
|
|
123
171
|
// Restore system.fetch.
|
|
124
172
|
if sysFetch != nil {
|
|
125
173
|
if sys, ok := opts["system"]; ok {
|
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
package utility
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import (
|
|
4
|
+
"strings"
|
|
5
|
+
|
|
6
|
+
vs "github.com/voxgig/struct"
|
|
7
|
+
|
|
8
|
+
"GOMODULE/core"
|
|
9
|
+
)
|
|
4
10
|
|
|
5
11
|
func prepareMethodUtil(ctx *core.Context) string {
|
|
6
12
|
opname := ctx.Op.Name
|
|
7
13
|
|
|
14
|
+
// The API definition is authoritative: a POST-only or PATCH-based API
|
|
15
|
+
// exposes `update` as POST or PATCH, not the PUT the op name implies.
|
|
16
|
+
// Only fall back to the op-name convention when the point has no method.
|
|
17
|
+
if m, ok := vs.GetProp(ctx.Point, "method").(string); ok && "" != m {
|
|
18
|
+
return strings.ToUpper(m)
|
|
19
|
+
}
|
|
20
|
+
|
|
8
21
|
methodMap := map[string]string{
|
|
9
22
|
"create": "POST",
|
|
10
23
|
"update": "PUT",
|
|
@@ -13,6 +13,7 @@ module SdkRuntime where
|
|
|
13
13
|
import Control.Exception (throwIO)
|
|
14
14
|
import Control.Monad (forM_, when)
|
|
15
15
|
import Data.Bits ((.&.))
|
|
16
|
+
import Data.Char (toUpper)
|
|
16
17
|
import Data.IORef
|
|
17
18
|
import qualified Data.Map.Strict as Map
|
|
18
19
|
import System.IO.Unsafe (unsafePerformIO)
|
|
@@ -318,12 +319,20 @@ featureInitUtil ctx f = do
|
|
|
318
319
|
|
|
319
320
|
-- ----- prepare / param -----
|
|
320
321
|
|
|
322
|
+
-- The API definition is authoritative: a POST-only or PATCH-based API
|
|
323
|
+
-- exposes `update` as POST or PATCH, not the PUT the op name implies. Only
|
|
324
|
+
-- fall back to the op-name convention when the point has no method.
|
|
321
325
|
prepareMethodUtil :: Context -> IO String
|
|
322
326
|
prepareMethodUtil ctx = do
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
+
point <- readIORef (cPoint ctx)
|
|
328
|
+
pm <- getp point "method"
|
|
329
|
+
case pm of
|
|
330
|
+
VStr m | not (null m) -> pure (map toUpper m)
|
|
331
|
+
_ -> do
|
|
332
|
+
op <- readIORef (cOp ctx)
|
|
333
|
+
pure $ case opName op of
|
|
334
|
+
"create" -> "POST"; "update" -> "PUT"; "load" -> "GET"
|
|
335
|
+
"list" -> "GET"; "remove" -> "DELETE"; "patch" -> "PATCH"; _ -> "GET"
|
|
327
336
|
|
|
328
337
|
prepareHeadersUtil :: Context -> IO Value
|
|
329
338
|
prepareHeadersUtil ctx = do
|
|
@@ -12,10 +12,10 @@ clean:
|
|
|
12
12
|
# --- Release: tag java/vX.Y.Z ----------------------------------------------
|
|
13
13
|
# Maven Central publication (Sonatype Central Portal) is pending for this
|
|
14
14
|
# target; publishing a version currently means pushing the java/vX.Y.Z git
|
|
15
|
-
# tag. Run headless under the
|
|
15
|
+
# tag. Run headless under the boru key vault, which injects the GitHub token
|
|
16
16
|
# so nothing lands on disk or argv:
|
|
17
|
-
#
|
|
18
|
-
#
|
|
17
|
+
# boru vault exec --for=github=<alias> -- make publish
|
|
18
|
+
# boru `--dry-run` injects a filler token we detect to rehearse (no tag/push).
|
|
19
19
|
# set -e + tag-last: a failed push never leaves a tag behind.
|
|
20
20
|
#
|
|
21
21
|
# NOTE: publishing the git tag is deliberately NOT gated on `mvn test` (every
|
|
@@ -23,14 +23,14 @@ clean:
|
|
|
23
23
|
# block the tag). Run `make test` separately in CI to gate quality.
|
|
24
24
|
VERSION := $(shell cat VERSION)
|
|
25
25
|
TAG := java/v$(VERSION)
|
|
26
|
-
|
|
26
|
+
BORU_DRY_RUN_FILLER := BORU-DRY-RUN-FILLER-NOT-A-REAL-SECRET
|
|
27
27
|
publish:
|
|
28
28
|
@if git rev-parse -q --verify "refs/tags/$(TAG)" >/dev/null; then \
|
|
29
29
|
echo "tag $(TAG) already exists — bump VERSION first"; exit 1; fi
|
|
30
30
|
@set -e; \
|
|
31
31
|
token="$${GITHUB_TOKEN:-$$GH_TOKEN}"; \
|
|
32
|
-
if [ "$$token" = "$(
|
|
33
|
-
echo "[dry-run]
|
|
32
|
+
if [ "$$token" = "$(BORU_DRY_RUN_FILLER)" ]; then \
|
|
33
|
+
echo "[dry-run] boru filler token detected: would tag $(TAG); nothing pushed."; \
|
|
34
34
|
exit 0; \
|
|
35
35
|
fi; \
|
|
36
36
|
git tag -a "$(TAG)" -m "java v$(VERSION)"; \
|
|
@@ -3,6 +3,7 @@ package JAVAPACKAGE.utility;
|
|
|
3
3
|
import java.util.Map;
|
|
4
4
|
|
|
5
5
|
import JAVAPACKAGE.core.Context;
|
|
6
|
+
import JAVAPACKAGE.utility.struct.Struct;
|
|
6
7
|
|
|
7
8
|
final class PrepareMethod {
|
|
8
9
|
|
|
@@ -19,6 +20,14 @@ final class PrepareMethod {
|
|
|
19
20
|
static String prepareMethod(Context ctx) {
|
|
20
21
|
String opname = ctx.op.name;
|
|
21
22
|
|
|
23
|
+
// The API definition is authoritative: a POST-only or PATCH-based API
|
|
24
|
+
// exposes `update` as POST or PATCH, not the PUT the op name implies.
|
|
25
|
+
// Only fall back to the op-name convention when the point has no method.
|
|
26
|
+
Object pm = Struct.getprop(ctx.point, "method");
|
|
27
|
+
if (pm instanceof String && !((String) pm).isEmpty()) {
|
|
28
|
+
return ((String) pm).toUpperCase();
|
|
29
|
+
}
|
|
30
|
+
|
|
22
31
|
String m = METHOD_MAP.get(opname);
|
|
23
32
|
if (m != null) {
|
|
24
33
|
return m;
|
|
@@ -10,23 +10,23 @@ clean:
|
|
|
10
10
|
rm -rf node_modules
|
|
11
11
|
|
|
12
12
|
# --- Release: publish to npm ----------------------------------------------
|
|
13
|
-
# Publish this package to npm, then tag js/vX.Y.Z. Run headless under the
|
|
13
|
+
# Publish this package to npm, then tag js/vX.Y.Z. Run headless under the boru
|
|
14
14
|
# key vault, which injects the npm auth token + the GitHub token (never on disk
|
|
15
15
|
# or argv):
|
|
16
|
-
#
|
|
17
|
-
#
|
|
16
|
+
# boru vault exec --for=npm=<alias> --for=github=<alias> -- make publish
|
|
17
|
+
# boru `--dry-run` injects a filler token we detect to rehearse (no publish/tag).
|
|
18
18
|
# set -e + tag-last: a failed publish never leaves a tag behind.
|
|
19
19
|
SHELL := /bin/bash
|
|
20
20
|
VERSION := $(shell node -p "require('./package.json').version")
|
|
21
21
|
TAG := js/v$(VERSION)
|
|
22
|
-
|
|
22
|
+
BORU_DRY_RUN_FILLER := BORU-DRY-RUN-FILLER-NOT-A-REAL-SECRET
|
|
23
23
|
publish: test
|
|
24
24
|
@if git rev-parse -q --verify "refs/tags/$(TAG)" >/dev/null; then \
|
|
25
25
|
echo "tag $(TAG) already exists — bump package.json version first"; exit 1; fi
|
|
26
26
|
@set -e; \
|
|
27
27
|
token="$${GITHUB_TOKEN:-$$GH_TOKEN}"; \
|
|
28
|
-
if [ "$$token" = "$(
|
|
29
|
-
echo "[dry-run]
|
|
28
|
+
if [ "$$token" = "$(BORU_DRY_RUN_FILLER)" ]; then \
|
|
29
|
+
echo "[dry-run] boru filler token detected: would publish to npm and tag $(TAG); nothing pushed."; \
|
|
30
30
|
exit 0; \
|
|
31
31
|
fi; \
|
|
32
32
|
npm stage publish --access public --registry https://registry.npmjs.org; \
|
|
@@ -79,16 +79,54 @@ function makeOptions(ctx) {
|
|
|
79
79
|
},
|
|
80
80
|
clean: {
|
|
81
81
|
keys: 'key,token,id'
|
|
82
|
+
},
|
|
83
|
+
// Server-variable values for a templated base URL (OpenAPI server
|
|
84
|
+
// variables): `{name}` placeholders in `base` are substituted from
|
|
85
|
+
// this map at construction. Spec defaults arrive via the generated
|
|
86
|
+
// Config; user values override them.
|
|
87
|
+
server: {
|
|
88
|
+
'`$CHILD`': ''
|
|
82
89
|
}
|
|
83
90
|
}
|
|
84
91
|
|
|
85
92
|
// JavaScript specific option values.
|
|
86
93
|
optspec.system.fetch = opts.system?.fetch || global.fetch
|
|
87
94
|
|
|
88
|
-
|
|
95
|
+
// Clone the config side before merging: `config` is a module-level
|
|
96
|
+
// singleton in ts/js, and merge would otherwise use its nested maps as
|
|
97
|
+
// merge TARGETS — one instance's options (server, headers, ...) would
|
|
98
|
+
// contaminate every instance constructed after it.
|
|
99
|
+
opts = merge([{}, struct.clone(cfgopts), opts])
|
|
89
100
|
|
|
90
101
|
opts = validate(opts, optspec)
|
|
91
102
|
|
|
103
|
+
// Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
|
|
104
|
+
// Every placeholder must resolve to a non-empty value: from
|
|
105
|
+
// options.server (user), else the Config default. A placeholder that
|
|
106
|
+
// resolves to '' is a construction ERROR in live mode — the URL cannot
|
|
107
|
+
// work — but in test mode substitutes the deterministic value
|
|
108
|
+
// `test-<name>` so offline tests need no configuration.
|
|
109
|
+
if ('string' === typeof opts.base && opts.base.includes('{')) {
|
|
110
|
+
const testmode = true === opts.test.active ||
|
|
111
|
+
true === (opts.feature && opts.feature.test && opts.feature.test.active)
|
|
112
|
+
const server = opts.server || {}
|
|
113
|
+
opts.base = opts.base.replace(/\{([A-Za-z0-9_]+)\}/g,
|
|
114
|
+
(_m, name) => {
|
|
115
|
+
let val = server[name]
|
|
116
|
+
val = 'string' === typeof val ? val : ''
|
|
117
|
+
if ('' === val) {
|
|
118
|
+
if (testmode) {
|
|
119
|
+
return 'test-' + name
|
|
120
|
+
}
|
|
121
|
+
throw new Error(
|
|
122
|
+
(config?.main?.name || 'SDK') + ": the server variable '" + name +
|
|
123
|
+
"' is required: the API base URL is '" + opts.base +
|
|
124
|
+
"' — pass { server: { " + name + ": '...' } } in the SDK options")
|
|
125
|
+
}
|
|
126
|
+
return val
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
92
130
|
// Resolve the feature add-order: an explicit array order (above) wins;
|
|
93
131
|
// otherwise order the map test-first, then the remaining names sorted, so
|
|
94
132
|
// the outcome is deterministic and `test` is always the base transport.
|
|
@@ -14,6 +14,15 @@ function prepareMethod(ctx) {
|
|
|
14
14
|
patch: 'PATCH',
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
// The API definition is authoritative: a POST-only or PATCH-based API
|
|
18
|
+
// exposes `update` as POST or PATCH, not the PUT the op name implies.
|
|
19
|
+
// Only fall back to the op-name convention when the point has no method.
|
|
20
|
+
const method = ctx.point && ctx.point.method
|
|
21
|
+
|
|
22
|
+
if (null != method && '' !== method) {
|
|
23
|
+
return method.toUpperCase()
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
return methodMap[key]
|
|
18
27
|
}
|
|
19
28
|
|
|
@@ -205,6 +205,9 @@ describe('PrimaryUtility', async () => {
|
|
|
205
205
|
body: 'present',
|
|
206
206
|
})
|
|
207
207
|
const reqClient = new SDK({
|
|
208
|
+
// Concrete base: a live construction must satisfy any server variables
|
|
209
|
+
// a templated base URL declares; a literal base sidesteps the requirement.
|
|
210
|
+
base: 'http://localhost:8080',
|
|
208
211
|
system: { fetch: mockFetch }
|
|
209
212
|
})
|
|
210
213
|
const reqUtility = reqClient.utility()
|
|
@@ -386,6 +389,7 @@ describe('PrimaryUtility', async () => {
|
|
|
386
389
|
test('fetcher-live', async () => {
|
|
387
390
|
const calls = []
|
|
388
391
|
const liveClient = new SDK({
|
|
392
|
+
base: 'http://localhost:8080',
|
|
389
393
|
system: {
|
|
390
394
|
fetch: async (url, init) => {
|
|
391
395
|
calls.push({ url, init })
|
|
@@ -407,6 +411,7 @@ describe('PrimaryUtility', async () => {
|
|
|
407
411
|
|
|
408
412
|
test('fetcher-blocked-test-mode', async () => {
|
|
409
413
|
const blockedClient = new SDK({
|
|
414
|
+
base: 'http://localhost:8080',
|
|
410
415
|
system: { fetch: async () => ({}) }
|
|
411
416
|
})
|
|
412
417
|
blockedClient._mode = 'test'
|
|
@@ -16,20 +16,20 @@ clean:
|
|
|
16
16
|
# --- Release: tag kotlin/vX.Y.Z ---------------------------------------------
|
|
17
17
|
# Maven Central publication (Sonatype Central Portal) is pending for this
|
|
18
18
|
# target; publishing a version currently means pushing the kotlin/vX.Y.Z git
|
|
19
|
-
# tag. Run headless under the
|
|
19
|
+
# tag. Run headless under the boru key vault, which injects the GitHub token
|
|
20
20
|
# so nothing lands on disk or argv:
|
|
21
|
-
#
|
|
22
|
-
#
|
|
21
|
+
# boru vault exec --for=github=<alias> -- make publish
|
|
22
|
+
# boru `--dry-run` injects a filler token we detect to rehearse (no tag/push).
|
|
23
23
|
VERSION := $(shell cat VERSION)
|
|
24
24
|
TAG := kotlin/v$(VERSION)
|
|
25
|
-
|
|
25
|
+
BORU_DRY_RUN_FILLER := BORU-DRY-RUN-FILLER-NOT-A-REAL-SECRET
|
|
26
26
|
publish:
|
|
27
27
|
@if git rev-parse -q --verify "refs/tags/$(TAG)" >/dev/null; then \
|
|
28
28
|
echo "tag $(TAG) already exists — bump VERSION first"; exit 1; fi
|
|
29
29
|
@set -e; \
|
|
30
30
|
token="$${GITHUB_TOKEN:-$$GH_TOKEN}"; \
|
|
31
|
-
if [ "$$token" = "$(
|
|
32
|
-
echo "[dry-run]
|
|
31
|
+
if [ "$$token" = "$(BORU_DRY_RUN_FILLER)" ]; then \
|
|
32
|
+
echo "[dry-run] boru filler token detected: would tag $(TAG); nothing pushed."; \
|
|
33
33
|
exit 0; \
|
|
34
34
|
fi; \
|
|
35
35
|
git tag -a "$(TAG)" -m "kotlin v$(VERSION)"; \
|
|
@@ -143,6 +143,15 @@ fun prepareHeaders(ctx: Context): MutableMap<String, Any?> {
|
|
|
143
143
|
|
|
144
144
|
fun prepareMethod(ctx: Context): String {
|
|
145
145
|
val opname = ctx.op.name
|
|
146
|
+
|
|
147
|
+
// The API definition is authoritative: a POST-only or PATCH-based API
|
|
148
|
+
// exposes `update` as POST or PATCH, not the PUT the op name implies.
|
|
149
|
+
// Only fall back to the op-name convention when the point has no method.
|
|
150
|
+
val pm = Struct.getprop(ctx.point, "method")
|
|
151
|
+
if (pm is String && "" != pm) {
|
|
152
|
+
return pm.uppercase()
|
|
153
|
+
}
|
|
154
|
+
|
|
146
155
|
val m = METHOD_MAP[opname]
|
|
147
156
|
if (m != null) {
|
|
148
157
|
return m
|
|
@@ -239,8 +239,13 @@ def preparePath (ctx : Value) : SIO String := do
|
|
|
239
239
|
pure ("/" ++ String.intercalate "/" segs.toList)
|
|
240
240
|
| _ => pure ""
|
|
241
241
|
|
|
242
|
+
/-- The API definition is authoritative: a POST-only or PATCH-based API
|
|
243
|
+
exposes `update` as POST or PATCH, not the PUT the op name implies. Only
|
|
244
|
+
fall back to the op-name convention when the point has no method. -/
|
|
242
245
|
def prepareMethod (ctx : Value) : SIO String := do
|
|
243
|
-
|
|
246
|
+
let m ← gpS (← gp ctx "point") "method"
|
|
247
|
+
if m != "" then pure m.toUpper
|
|
248
|
+
else pure (opMethodOf (← opnameOf ctx))
|
|
244
249
|
|
|
245
250
|
def prepareHeaders (ctx : Value) : SIO Value := do
|
|
246
251
|
let options ← gp ctx "options"
|