@voxgig/sdkgen 3.5.0 → 3.6.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/ExternalTarget.js +1 -1
- package/dist/cmp/ExternalTarget.js.map +1 -1
- package/dist/helpers/naming.d.ts +4 -1
- package/dist/helpers/naming.js +119 -5
- package/dist/helpers/naming.js.map +1 -1
- package/dist/helpers/opShape.js +72 -0
- package/dist/helpers/opShape.js.map +1 -1
- package/dist/helpers/packageMeta.d.ts +2 -1
- package/dist/helpers/packageMeta.js +57 -20
- package/dist/helpers/packageMeta.js.map +1 -1
- package/dist/sdkgen.d.ts +3 -3
- package/dist/sdkgen.js +61 -18
- package/dist/sdkgen.js.map +1 -1
- package/dist/testkit.d.ts +39 -0
- package/dist/testkit.js +663 -0
- package/dist/testkit.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +32 -8
- package/project/.sdk/src/cmp/c/Test_c.ts +5 -7
- package/project/.sdk/src/cmp/clojure/Test_clojure.ts +3 -7
- package/project/.sdk/src/cmp/cpp/Test_cpp.ts +4 -7
- package/project/.sdk/src/cmp/csharp/Package_csharp.ts +1 -1
- package/project/.sdk/src/cmp/csharp/Test_csharp.ts +5 -7
- package/project/.sdk/src/cmp/dart/Test_dart.ts +3 -4
- package/project/.sdk/src/cmp/elixir/Package_elixir.ts +1 -1
- package/project/.sdk/src/cmp/elixir/Test_elixir.ts +5 -6
- package/project/.sdk/src/cmp/go/EntityTypes_go.ts +1 -1
- package/project/.sdk/src/cmp/go/Entity_go.ts +1 -1
- package/project/.sdk/src/cmp/go/Gitignore_go.ts +6 -0
- package/project/.sdk/src/cmp/go/Main_go.ts +2 -2
- package/project/.sdk/src/cmp/go/Package_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeExamplesTest_go.ts +59 -6
- package/project/.sdk/src/cmp/go/ReadmeExplanation_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeHowto_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeInstall_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeModel_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeTopTest_go.ts +1 -1
- package/project/.sdk/src/cmp/go/Test_go.ts +6 -8
- package/project/.sdk/src/cmp/go-cli/Main_go-cli.ts +6 -2
- package/project/.sdk/src/cmp/go-mcp/Main_go-mcp.ts +6 -2
- package/project/.sdk/src/cmp/java/Test_java.ts +4 -7
- package/project/.sdk/src/cmp/js/Package_js.ts +1 -1
- package/project/.sdk/src/cmp/js/Test_js.ts +4 -7
- package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +4 -7
- package/project/.sdk/src/cmp/lean/Test_lean.ts +3 -3
- package/project/.sdk/src/cmp/lua/Package_lua.ts +2 -2
- package/project/.sdk/src/cmp/lua/Test_lua.ts +5 -7
- package/project/.sdk/src/cmp/ocaml/Test_ocaml.ts +3 -7
- package/project/.sdk/src/cmp/perl/Package_perl.ts +1 -1
- package/project/.sdk/src/cmp/perl/Test_perl.ts +5 -7
- package/project/.sdk/src/cmp/php/EntityTypes_php.ts +13 -2
- package/project/.sdk/src/cmp/php/Package_php.ts +2 -2
- package/project/.sdk/src/cmp/php/Test_php.ts +5 -7
- package/project/.sdk/src/cmp/py/Package_py.ts +2 -2
- package/project/.sdk/src/cmp/py/Test_py.ts +5 -7
- package/project/.sdk/src/cmp/py-data/Main_py-data.ts +14 -19
- package/project/.sdk/src/cmp/rb/Package_rb.ts +3 -3
- package/project/.sdk/src/cmp/rb/ReadmeInstall_rb.ts +1 -1
- package/project/.sdk/src/cmp/rb/Test_rb.ts +5 -7
- package/project/.sdk/src/cmp/rust/Test_rust.ts +5 -7
- package/project/.sdk/src/cmp/scala/Test_scala.ts +5 -2
- package/project/.sdk/src/cmp/seneca-provider/Gitignore_seneca-provider.ts +161 -0
- package/project/.sdk/src/cmp/seneca-provider/Main_seneca-provider.ts +20 -11
- package/project/.sdk/src/cmp/swift/Test_swift.ts +5 -7
- package/project/.sdk/src/cmp/ts/Package_ts.ts +1 -1
- package/project/.sdk/src/cmp/ts/Test_ts.ts +8 -7
- package/project/.sdk/src/cmp/zig/Test_zig.ts +3 -7
- package/project/.sdk/tm/c/utility/make_point.c +64 -3
- package/project/.sdk/tm/clojure/src/sdk/core.clj +49 -14
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +55 -3
- package/project/.sdk/tm/csharp/feature/TestFeature.cs +36 -2
- package/project/.sdk/tm/csharp/utility/MakePoint.cs +65 -2
- package/project/.sdk/tm/dart/lib/feature/test/TestFeature.dart +34 -4
- package/project/.sdk/tm/dart/lib/utility/MakePointUtility.dart +48 -2
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +70 -10
- package/project/.sdk/tm/go/feature/test_feature.go +34 -2
- package/project/.sdk/tm/go/utility/make_point.go +66 -2
- package/project/.sdk/tm/java/feature/TestFeature.java +42 -2
- package/project/.sdk/tm/java/utility/MakePoint.java +61 -2
- package/project/.sdk/tm/js/src/feature/test/TestFeature.js +22 -3
- package/project/.sdk/tm/js/src/utility/MakePointUtility.js +72 -2
- package/project/.sdk/tm/kotlin/feature/TestFeature.kt +31 -2
- package/project/.sdk/tm/kotlin/utility/MakePoint.kt +58 -2
- package/project/.sdk/tm/lean/src/SdkUtility.lean +28 -0
- package/project/.sdk/tm/lua/utility/make_point.lua +65 -2
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +40 -2
- package/project/.sdk/tm/perl/utility/make_point.pm +55 -2
- package/project/.sdk/tm/php/feature/TestFeature.php +30 -1
- package/project/.sdk/tm/php/utility/MakePoint.php +49 -1
- package/project/.sdk/tm/py/pkg/utility/make_point.py +56 -2
- package/project/.sdk/tm/rb/utility/make_point.rb +42 -2
- package/project/.sdk/tm/rust/utility/make_point.rs +69 -2
- package/project/.sdk/tm/scala/feature/TestFeature.scala +36 -1
- package/project/.sdk/tm/scala/utility/Make.scala +57 -3
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/TestFeature.swift +29 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Make.swift +51 -2
- package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +22 -3
- package/project/.sdk/tm/ts/src/utility/MakePointUtility.ts +72 -2
- package/project/.sdk/tm/zig/core/utility.zig +66 -3
- package/project/sdkgen-package.json +1 -2
- package/src/cmp/ExternalTarget.ts +1 -1
- package/src/helpers/naming.ts +129 -5
- package/src/helpers/opShape.ts +85 -0
- package/src/helpers/packageMeta.ts +59 -20
- package/src/sdkgen.ts +68 -20
- package/src/testkit.ts +863 -0
- package/testkit.d.ts +3 -0
- package/testkit.js +12 -0
- package/project/.sdk/model/target/haskell.aontu +0 -72
- package/project/.sdk/src/cmp/haskell/Config_haskell.ts +0 -98
- package/project/.sdk/src/cmp/haskell/Entity_haskell.ts +0 -13
- package/project/.sdk/src/cmp/haskell/Gitignore_haskell.ts +0 -27
- package/project/.sdk/src/cmp/haskell/Main_haskell.ts +0 -123
- package/project/.sdk/src/cmp/haskell/Package_haskell.ts +0 -60
- package/project/.sdk/src/cmp/haskell/ReadmeEntity_haskell.ts +0 -186
- package/project/.sdk/src/cmp/haskell/ReadmeExamplesTest_haskell.ts +0 -135
- package/project/.sdk/src/cmp/haskell/ReadmeExplanation_haskell.ts +0 -48
- package/project/.sdk/src/cmp/haskell/ReadmeHowto_haskell.ts +0 -168
- package/project/.sdk/src/cmp/haskell/ReadmeInstall_haskell.ts +0 -51
- package/project/.sdk/src/cmp/haskell/ReadmeIntro_haskell.ts +0 -67
- package/project/.sdk/src/cmp/haskell/ReadmeModel_haskell.ts +0 -162
- package/project/.sdk/src/cmp/haskell/ReadmeOptions_haskell.ts +0 -74
- package/project/.sdk/src/cmp/haskell/ReadmeQuick_haskell.ts +0 -241
- package/project/.sdk/src/cmp/haskell/ReadmeRef_haskell.ts +0 -408
- package/project/.sdk/src/cmp/haskell/ReadmeTopHowto_haskell.ts +0 -31
- package/project/.sdk/src/cmp/haskell/ReadmeTopQuick_haskell.ts +0 -104
- package/project/.sdk/src/cmp/haskell/ReadmeTopTest_haskell.ts +0 -80
- package/project/.sdk/src/cmp/haskell/Test_haskell.ts +0 -300
- package/project/.sdk/src/cmp/haskell/tsconfig.json +0 -15
- package/project/.sdk/src/cmp/haskell/utility_haskell.ts +0 -166
- package/project/.sdk/tm/haskell/LICENSE +0 -21
- package/project/.sdk/tm/haskell/Makefile +0 -22
- package/project/.sdk/tm/haskell/VERSION +0 -1
- package/project/.sdk/tm/haskell/src/SdkFeatures.hs +0 -1440
- package/project/.sdk/tm/haskell/src/SdkHelpers.hs +0 -337
- package/project/.sdk/tm/haskell/src/SdkJson.hs +0 -111
- package/project/.sdk/tm/haskell/src/SdkRuntime.hs +0 -1219
- package/project/.sdk/tm/haskell/src/SdkTypes.hs +0 -195
- package/project/.sdk/tm/haskell/src/VoxgigStruct.hs +0 -2299
- package/project/.sdk/tm/haskell/src/Vregex.hs +0 -237
- package/project/.sdk/tm/haskell/src/feature/README.md +0 -5
- package/project/.sdk/tm/haskell/src/feature/audit/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/base/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/cache/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/clienttrack/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/debug/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/idempotency/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/log/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/metrics/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/netsim/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/paging/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/proxy/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/ratelimit/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/rbac/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/retry/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/streaming/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/telemetry/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/test/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/timeout/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/test/Harness.hs +0 -281
- package/project/.sdk/tm/haskell/test/Runner.hs +0 -47
- package/project/.sdk/tm/haskell/test/StructCorpus.hs +0 -449
- package/project/.sdk/tm/haskell/test/TCustomUtility.hs +0 -38
- package/project/.sdk/tm/haskell/test/TFeature.hs +0 -794
- package/project/.sdk/tm/haskell/test/TNetsim.hs +0 -50
- package/project/.sdk/tm/haskell/test/TPipeline.hs +0 -334
- package/project/.sdk/tm/haskell/test/TPrimaryUtility.hs +0 -386
- package/project/.sdk/tm/haskell/test/Testutil.hs +0 -59
|
@@ -4,6 +4,27 @@ import KOTLINPACKAGE.core.Context
|
|
|
4
4
|
import KOTLINPACKAGE.core.Helpers
|
|
5
5
|
import KOTLINPACKAGE.utility.struct.Struct
|
|
6
6
|
|
|
7
|
+
// How many path segments a point has.
|
|
8
|
+
private fun partsLen(point: Map<String, Any?>?): Int {
|
|
9
|
+
val parts = Struct.getprop(point, "parts")
|
|
10
|
+
return if (parts is List<*>) parts.size else 0
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Does this point's path end in a parameter? A record route ends in the
|
|
14
|
+
// record's identifier (/boards/{id}); a cross-reference that also returns the
|
|
15
|
+
// entity ends in the relationship's name (/posts/{id}/author). That, then
|
|
16
|
+
// fewest segments, is what tells the entity's own route from a
|
|
17
|
+
// cross-reference. The same rule runs at generation time, in
|
|
18
|
+
// helpers/opShape.ts — both sides must move together.
|
|
19
|
+
private fun terminalParam(point: Map<String, Any?>?): Boolean {
|
|
20
|
+
val parts = Struct.getprop(point, "parts")
|
|
21
|
+
if (parts !is List<*> || parts.isEmpty()) {
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
val last = parts[parts.size - 1]
|
|
25
|
+
return last is String && last.startsWith("{")
|
|
26
|
+
}
|
|
27
|
+
|
|
7
28
|
@Suppress("UNCHECKED_CAST")
|
|
8
29
|
fun makePoint(ctx: Context): Map<String, Any?> {
|
|
9
30
|
val outPoint = ctx.out["point"]
|
|
@@ -55,9 +76,10 @@ fun makePoint(ctx: Context): Map<String, Any?> {
|
|
|
55
76
|
}
|
|
56
77
|
|
|
57
78
|
var point: MutableMap<String, Any?>? = null
|
|
79
|
+
var matched = false
|
|
58
80
|
for (i in op.points.indices) {
|
|
59
|
-
|
|
60
|
-
val selectDef = Helpers.toMapAny(Struct.getprop(
|
|
81
|
+
val cand = op.points[i]
|
|
82
|
+
val selectDef = Helpers.toMapAny(Struct.getprop(cand, "select"))
|
|
61
83
|
var found = true
|
|
62
84
|
|
|
63
85
|
if (selectDef != null) {
|
|
@@ -84,10 +106,44 @@ fun makePoint(ctx: Context): Map<String, Any?> {
|
|
|
84
106
|
}
|
|
85
107
|
|
|
86
108
|
if (found) {
|
|
109
|
+
point = cand
|
|
110
|
+
matched = true
|
|
87
111
|
break
|
|
88
112
|
}
|
|
89
113
|
}
|
|
90
114
|
|
|
115
|
+
// select.exist can list more than the params needed to pick a point, so
|
|
116
|
+
// nothing matches — fall back to the entity's own route rather than
|
|
117
|
+
// whichever point came last.
|
|
118
|
+
if (!matched) {
|
|
119
|
+
// A request naming an action reaches here only because that action's
|
|
120
|
+
// own point failed its exist test, so it is unbuildable whatever we
|
|
121
|
+
// pick. Refuse it BEFORE choosing a fallback: the guard below compares
|
|
122
|
+
// the chosen point's $action and would wave the request through
|
|
123
|
+
// whenever the fallback lands on the action point itself.
|
|
124
|
+
val unmatchedAction = Struct.getprop(reqselector, "\$action", null)
|
|
125
|
+
if (unmatchedAction != null) {
|
|
126
|
+
throw ctx.makeError(
|
|
127
|
+
"point_action_invalid",
|
|
128
|
+
"Operation \"" + op.name +
|
|
129
|
+
"\" action \"" + Struct.stringify(unmatchedAction) + "\" is not valid.",
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
point = op.points[0]
|
|
134
|
+
for (cand in op.points) {
|
|
135
|
+
val candTerm = terminalParam(cand)
|
|
136
|
+
val bestTerm = terminalParam(point)
|
|
137
|
+
if (candTerm != bestTerm) {
|
|
138
|
+
if (candTerm) {
|
|
139
|
+
point = cand
|
|
140
|
+
}
|
|
141
|
+
} else if (partsLen(cand) < partsLen(point)) {
|
|
142
|
+
point = cand
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
91
147
|
val reqAction = Struct.getprop(reqselector, "\$action", null)
|
|
92
148
|
if (reqAction != null && point != null) {
|
|
93
149
|
val pointSelect = Helpers.toMapAny(Struct.getprop(point, "select"))
|
|
@@ -597,6 +597,20 @@ def makeResult (ctx : Value) : SIO Value := do
|
|
|
597
597
|
|
|
598
598
|
/-- Select the endpoint for this operation: the single point, else the first
|
|
599
599
|
whose `select.exist` keys are all present and whose `$action` agrees. -/
|
|
600
|
+
/-- How many path segments a point has, and whether its path ends in a
|
|
601
|
+
parameter. A record route ends in the record's identifier (/boards/{id});
|
|
602
|
+
a cross-reference that also returns the entity ends in the relationship's
|
|
603
|
+
name (/posts/{id}/author). That, then fewest segments, is what tells the
|
|
604
|
+
entity's own route from a cross-reference. The same rule runs at
|
|
605
|
+
generation time, in helpers/opShape.ts — both sides must move together. -/
|
|
606
|
+
def pointShape (pt : Value) : SIO (Nat × Bool) := do
|
|
607
|
+
match (← gp pt "parts") with
|
|
608
|
+
| .list i =>
|
|
609
|
+
let items ← listItems i
|
|
610
|
+
if items.size == 0 then pure (0, false)
|
|
611
|
+
else pure (items.size, (vs items[items.size - 1]!).startsWith "{")
|
|
612
|
+
| _ => pure (0, false)
|
|
613
|
+
|
|
600
614
|
def makePoint (ctx : Value) : SIO Value := do
|
|
601
615
|
let op ← gp ctx "op"
|
|
602
616
|
let matchV ← gp ctx "reqmatch"
|
|
@@ -622,6 +636,20 @@ def makePoint (ctx : Value) : SIO Value := do
|
|
|
622
636
|
| _ => pure ()
|
|
623
637
|
if ok && ((← gp sel "$action") == (← gp matchV "$action")) then
|
|
624
638
|
chosen := pt
|
|
639
|
+
-- select.exist can list more than the params needed to pick a point (for
|
|
640
|
+
-- /boards/{id} it is Trello's 17 optional query-includes), so a plain
|
|
641
|
+
-- {id} call matches NOTHING and this loop chose no point at all. Fall
|
|
642
|
+
-- back to the entity's own route rather than sending nowhere.
|
|
643
|
+
if isNov chosen then
|
|
644
|
+
let mut best := pts[0]!
|
|
645
|
+
for cand in pts do
|
|
646
|
+
let (candLen, candTerm) ← pointShape cand
|
|
647
|
+
let (bestLen, bestTerm) ← pointShape best
|
|
648
|
+
if candTerm && !bestTerm then
|
|
649
|
+
best := cand
|
|
650
|
+
else if candTerm == bestTerm && candLen < bestLen then
|
|
651
|
+
best := cand
|
|
652
|
+
chosen := best
|
|
625
653
|
if !(isNov chosen) then sp ctx "point" chosen
|
|
626
654
|
pure chosen
|
|
627
655
|
|
|
@@ -3,6 +3,32 @@
|
|
|
3
3
|
local vs = require("utility.struct.struct")
|
|
4
4
|
local helpers = require("core.helpers")
|
|
5
5
|
|
|
6
|
+
-- How many path segments a point has.
|
|
7
|
+
local function parts_len(point)
|
|
8
|
+
local parts = vs.getprop(point, "parts")
|
|
9
|
+
if type(parts) == "table" then
|
|
10
|
+
return #parts
|
|
11
|
+
end
|
|
12
|
+
return 0
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
-- Does this point's path end in a parameter? A record route ends in the
|
|
17
|
+
-- record's identifier (/boards/{id}); a cross-reference that also returns
|
|
18
|
+
-- the entity ends in the relationship's name (/posts/{id}/author). That,
|
|
19
|
+
-- then fewest segments, is what tells the entity's own route from a
|
|
20
|
+
-- cross-reference. The same rule runs at generation time, in
|
|
21
|
+
-- helpers/opShape.ts — both sides must move together.
|
|
22
|
+
local function terminal_param(point)
|
|
23
|
+
local parts = vs.getprop(point, "parts")
|
|
24
|
+
if type(parts) ~= "table" or #parts == 0 then
|
|
25
|
+
return false
|
|
26
|
+
end
|
|
27
|
+
local last = parts[#parts]
|
|
28
|
+
return type(last) == "string" and last:sub(1, 1) == "{"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
|
|
6
32
|
local function make_point_util(ctx)
|
|
7
33
|
if ctx.out["point"] ~= nil then
|
|
8
34
|
local preset = ctx.out["point"]
|
|
@@ -44,9 +70,10 @@ local function make_point_util(ctx)
|
|
|
44
70
|
end
|
|
45
71
|
|
|
46
72
|
local point = nil
|
|
73
|
+
local matched = false
|
|
47
74
|
for i = 1, #op.points do
|
|
48
|
-
|
|
49
|
-
local select_def = helpers.to_map(vs.getprop(
|
|
75
|
+
local cand = op.points[i]
|
|
76
|
+
local select_def = helpers.to_map(vs.getprop(cand, "select"))
|
|
50
77
|
local found = true
|
|
51
78
|
|
|
52
79
|
if selector ~= nil and select_def ~= nil then
|
|
@@ -73,10 +100,46 @@ local function make_point_util(ctx)
|
|
|
73
100
|
end
|
|
74
101
|
|
|
75
102
|
if found then
|
|
103
|
+
point = cand
|
|
104
|
+
matched = true
|
|
76
105
|
break
|
|
77
106
|
end
|
|
78
107
|
end
|
|
79
108
|
|
|
109
|
+
-- select.exist can list more than the params needed to pick a point, so
|
|
110
|
+
-- nothing matches — fall back to the entity's own route rather than
|
|
111
|
+
-- whichever point came last.
|
|
112
|
+
if not matched then
|
|
113
|
+
-- A request naming an action reaches here only because that action's
|
|
114
|
+
-- own point failed its exist test, so it is unbuildable whatever we
|
|
115
|
+
-- pick. Refuse it BEFORE choosing a fallback: the guard below compares
|
|
116
|
+
-- the chosen point's $action and would wave the request through
|
|
117
|
+
-- whenever the fallback lands on the action point itself.
|
|
118
|
+
local unmatched_action = nil
|
|
119
|
+
if reqselector ~= nil then
|
|
120
|
+
unmatched_action = vs.getprop(reqselector, "$action")
|
|
121
|
+
end
|
|
122
|
+
if unmatched_action ~= nil then
|
|
123
|
+
return nil, ctx:make_error("point_action_invalid",
|
|
124
|
+
'Operation "' .. op.name ..
|
|
125
|
+
'" action "' .. vs.stringify(unmatched_action) .. '" is not valid.')
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
point = op.points[1]
|
|
129
|
+
for i = 1, #op.points do
|
|
130
|
+
local cand = op.points[i]
|
|
131
|
+
local cand_term = terminal_param(cand)
|
|
132
|
+
local best_term = terminal_param(point)
|
|
133
|
+
if cand_term ~= best_term then
|
|
134
|
+
if cand_term then
|
|
135
|
+
point = cand
|
|
136
|
+
end
|
|
137
|
+
elseif parts_len(cand) < parts_len(point) then
|
|
138
|
+
point = cand
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
80
143
|
if reqselector ~= nil then
|
|
81
144
|
local req_action = vs.getprop(reqselector, "$action")
|
|
82
145
|
if req_action ~= nil and point ~= nil then
|
|
@@ -636,12 +636,12 @@ let make_point_util (ctx : ctx) : (value * sdk_error option) =
|
|
|
636
636
|
if op.op_input = "data" then ctx.c_reqdata, ctx.c_data
|
|
637
637
|
else ctx.c_reqmatch, ctx.c_match in
|
|
638
638
|
let chosen = ref Noval in
|
|
639
|
+
let matched = ref false in
|
|
639
640
|
let n = List.length points in
|
|
640
641
|
let arr = Array.of_list points in
|
|
641
642
|
let i = ref 0 and stop = ref false in
|
|
642
643
|
while not !stop && !i < n do
|
|
643
644
|
let point = arr.(!i) in
|
|
644
|
-
chosen := point;
|
|
645
645
|
let select_def = to_map (getp point "select") in
|
|
646
646
|
let found = ref true in
|
|
647
647
|
(match select_def with
|
|
@@ -661,8 +661,46 @@ let make_point_util (ctx : ctx) : (value * sdk_error option) =
|
|
|
661
661
|
let select_action = getp select_def "$action" in
|
|
662
662
|
if req_action <> select_action then found := false
|
|
663
663
|
end;
|
|
664
|
-
if !found then
|
|
664
|
+
if !found then begin chosen := point; matched := true; stop := true end
|
|
665
|
+
else incr i
|
|
665
666
|
done;
|
|
667
|
+
(* select.exist can list more than the params needed to pick a point
|
|
668
|
+
(for /boards/{id} it is Trello's 17 optional query-includes), so a
|
|
669
|
+
plain {id} call matches NOTHING. Fall back to the entity's own
|
|
670
|
+
route rather than whichever point came last. *)
|
|
671
|
+
if not !matched then begin
|
|
672
|
+
(* A request naming an action reaches here only because that
|
|
673
|
+
action's own point failed its exist test, so it is unbuildable
|
|
674
|
+
whatever we pick. Refuse it BEFORE choosing a fallback: the guard
|
|
675
|
+
below compares the chosen point's $action and would wave the
|
|
676
|
+
request through whenever the fallback lands on the action point
|
|
677
|
+
itself. *)
|
|
678
|
+
let unmatched_action = getp reqselector "$action" in
|
|
679
|
+
if not (is_noval unmatched_action) then
|
|
680
|
+
raise (Sdk_error_exc (ctx_make_error ctx "point_action_invalid"
|
|
681
|
+
("Operation \"" ^ op.op_name ^ "\" action \"" ^ (stringify unmatched_action) ^ "\" is not valid.")));
|
|
682
|
+
(* A terminal parameter marks a record route (/boards/{id}); a
|
|
683
|
+
cross-reference ends in the relationship's name
|
|
684
|
+
(/posts/{id}/author). Failing that, the shallower path wins. The
|
|
685
|
+
same rule runs at generation time, in helpers/opShape.ts — both
|
|
686
|
+
sides must move together. *)
|
|
687
|
+
let parts_len p =
|
|
688
|
+
match getp p "parts" with List r -> List.length !r | _ -> 0 in
|
|
689
|
+
let terminal_param p =
|
|
690
|
+
match getp p "parts" with
|
|
691
|
+
| List r ->
|
|
692
|
+
(match List.rev !r with
|
|
693
|
+
| last :: _ ->
|
|
694
|
+
let s = vstring last in
|
|
695
|
+
String.length s > 0 && s.[0] = '{'
|
|
696
|
+
| [] -> false)
|
|
697
|
+
| _ -> false in
|
|
698
|
+
chosen := arr.(0);
|
|
699
|
+
Array.iter (fun cand ->
|
|
700
|
+
let ct = terminal_param cand and bt = terminal_param !chosen in
|
|
701
|
+
if ct <> bt then (if ct then chosen := cand)
|
|
702
|
+
else if parts_len cand < parts_len !chosen then chosen := cand) arr
|
|
703
|
+
end;
|
|
666
704
|
let req_action = getp reqselector "$action" in
|
|
667
705
|
if not (is_noval req_action) && not (is_noval !chosen) then begin
|
|
668
706
|
let point_select = to_map (getp !chosen "select") in
|
|
@@ -17,6 +17,27 @@ package ProjectNameUtilities;
|
|
|
17
17
|
|
|
18
18
|
our %REGISTRY;
|
|
19
19
|
|
|
20
|
+
# How many path segments a point has.
|
|
21
|
+
sub _parts_len {
|
|
22
|
+
my ($point) = @_;
|
|
23
|
+
my $parts = ProjectNameHelpers::gp($point, 'parts');
|
|
24
|
+
return Voxgig::Struct::islist($parts) ? scalar(@$parts) : 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Does this point's path end in a parameter? A record route ends in the
|
|
28
|
+
# record's identifier (/boards/{id}); a cross-reference that also returns the
|
|
29
|
+
# entity ends in the relationship's name (/posts/{id}/author). That, then
|
|
30
|
+
# fewest segments, is what tells the entity's own route from a
|
|
31
|
+
# cross-reference. The same rule runs at generation time, in
|
|
32
|
+
# helpers/opShape.ts — both sides must move together.
|
|
33
|
+
sub _terminal_param {
|
|
34
|
+
my ($point) = @_;
|
|
35
|
+
my $parts = ProjectNameHelpers::gp($point, 'parts');
|
|
36
|
+
return 0 unless Voxgig::Struct::islist($parts) && 0 < scalar(@$parts);
|
|
37
|
+
my $last = $parts->[-1];
|
|
38
|
+
return (defined $last && !ref $last && $last =~ /^\{/) ? 1 : 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
20
41
|
$REGISTRY{make_point} = sub {
|
|
21
42
|
my ($ctx) = @_;
|
|
22
43
|
|
|
@@ -54,8 +75,8 @@ $REGISTRY{make_point} = sub {
|
|
|
54
75
|
my $selector = ('data' eq $op->{input}) ? $ctx->{data} : $ctx->{match};
|
|
55
76
|
|
|
56
77
|
my $point;
|
|
78
|
+
my $matched = 0;
|
|
57
79
|
for my $p (@{ $op->{points} }) {
|
|
58
|
-
$point = $p;
|
|
59
80
|
my $select_def = ProjectNameHelpers::to_map(ProjectNameHelpers::gp($p, 'select'));
|
|
60
81
|
my $found = 1;
|
|
61
82
|
|
|
@@ -79,7 +100,39 @@ $REGISTRY{make_point} = sub {
|
|
|
79
100
|
$found = 0 unless ProjectNameHelpers::eqv($req_action, $select_action);
|
|
80
101
|
}
|
|
81
102
|
|
|
82
|
-
|
|
103
|
+
if ($found) {
|
|
104
|
+
$point = $p;
|
|
105
|
+
$matched = 1;
|
|
106
|
+
last;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# select.exist can list more than the params needed to pick a point, so
|
|
111
|
+
# nothing matches — fall back to the entity's own route rather than
|
|
112
|
+
# whichever point came last.
|
|
113
|
+
unless ($matched) {
|
|
114
|
+
# A request naming an action reaches here only because that action's
|
|
115
|
+
# own point failed its exist test, so it is unbuildable whatever we
|
|
116
|
+
# pick. Refuse it BEFORE choosing a fallback: the guard below compares
|
|
117
|
+
# the chosen point's $action and would wave the request through
|
|
118
|
+
# whenever the fallback lands on the action point itself.
|
|
119
|
+
my $unmatched_action = $reqselector
|
|
120
|
+
? ProjectNameHelpers::gp($reqselector, '$action') : undef;
|
|
121
|
+
if (defined $unmatched_action) {
|
|
122
|
+
return (undef, $ctx->make_error('point_action_invalid',
|
|
123
|
+
'Operation "' . $op->{name} . '" action "' .
|
|
124
|
+
Voxgig::Struct::stringify($unmatched_action) . '" is not valid.'));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
$point = $op->{points}[0];
|
|
128
|
+
for my $p (@{ $op->{points} }) {
|
|
129
|
+
if (_terminal_param($p) != _terminal_param($point)) {
|
|
130
|
+
$point = $p if _terminal_param($p);
|
|
131
|
+
}
|
|
132
|
+
elsif (_parts_len($p) < _parts_len($point)) {
|
|
133
|
+
$point = $p;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
83
136
|
}
|
|
84
137
|
|
|
85
138
|
if ($reqselector) {
|
|
@@ -369,11 +369,40 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
369
369
|
// any missing piece falls back to "no required params".
|
|
370
370
|
$reqd_names = [];
|
|
371
371
|
if (is_string($opname) && is_string($entityName) && isset($ctx->config)) {
|
|
372
|
+
// Pick the entity's own endpoint, not a cross-reference from
|
|
373
|
+
// another resource that also returns it — the same rule
|
|
374
|
+
// MakePoint falls back to, so the seed-data query is built from
|
|
375
|
+
// the endpoint the request will actually be sent to: a terminal
|
|
376
|
+
// `{id}` marks a record route, and failing that the shallower
|
|
377
|
+
// path wins.
|
|
372
378
|
$points = \Voxgig\Struct\Struct::getpath(
|
|
373
379
|
['entity', $entityName, 'op', $opname, 'points'],
|
|
374
380
|
$ctx->config
|
|
375
381
|
);
|
|
376
|
-
$point = \Voxgig\Struct\Struct::getelem($points,
|
|
382
|
+
$point = \Voxgig\Struct\Struct::getelem($points, 0);
|
|
383
|
+
if (is_array($points)) {
|
|
384
|
+
$point_parts_len = function ($p) {
|
|
385
|
+
$parts = \Voxgig\Struct\Struct::getprop($p, 'parts');
|
|
386
|
+
return is_array($parts) ? count($parts) : 0;
|
|
387
|
+
};
|
|
388
|
+
$point_terminal_param = function ($p) {
|
|
389
|
+
$parts = \Voxgig\Struct\Struct::getprop($p, 'parts');
|
|
390
|
+
if (!is_array($parts) || 0 === count($parts)) {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
$last = $parts[count($parts) - 1];
|
|
394
|
+
return is_string($last) && 0 === strpos($last, '{');
|
|
395
|
+
};
|
|
396
|
+
foreach ($points as $cand) {
|
|
397
|
+
if ($point_terminal_param($cand) !== $point_terminal_param($point)) {
|
|
398
|
+
if ($point_terminal_param($cand)) {
|
|
399
|
+
$point = $cand;
|
|
400
|
+
}
|
|
401
|
+
} elseif ($point_parts_len($cand) < $point_parts_len($point)) {
|
|
402
|
+
$point = $cand;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
377
406
|
$params = is_array($point) ? ($point['args']['params'] ?? null) : null;
|
|
378
407
|
if (is_array($params)) {
|
|
379
408
|
foreach ($params as $p) {
|
|
@@ -42,8 +42,8 @@ class ProjectNameMakePoint
|
|
|
42
42
|
$selector = $op->input === 'data' ? $ctx->data : $ctx->match;
|
|
43
43
|
|
|
44
44
|
$point = null;
|
|
45
|
+
$matched = false;
|
|
45
46
|
foreach ($op->points as $p) {
|
|
46
|
-
$point = $p;
|
|
47
47
|
$select_def = ProjectNameHelpers::to_map(\Voxgig\Struct\Struct::getprop($p, 'select'));
|
|
48
48
|
$found = true;
|
|
49
49
|
|
|
@@ -70,10 +70,58 @@ class ProjectNameMakePoint
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
if ($found) {
|
|
73
|
+
$point = $p;
|
|
74
|
+
$matched = true;
|
|
73
75
|
break;
|
|
74
76
|
}
|
|
75
77
|
}
|
|
76
78
|
|
|
79
|
+
// select.exist can list more than the params needed to pick a
|
|
80
|
+
// point, so nothing matches — fall back to the entity's own
|
|
81
|
+
// route rather than the last point.
|
|
82
|
+
if (!$matched) {
|
|
83
|
+
// A request naming an action reaches here only because that
|
|
84
|
+
// action's own point failed its exist test, so it is
|
|
85
|
+
// unbuildable whatever we pick. Refuse it BEFORE choosing a
|
|
86
|
+
// fallback: the guard below compares the chosen point's
|
|
87
|
+
// $action and would wave the request through whenever the
|
|
88
|
+
// fallback lands on the action point itself.
|
|
89
|
+
$unmatched_action = $reqselector
|
|
90
|
+
? \Voxgig\Struct\Struct::getprop($reqselector, '$action') : null;
|
|
91
|
+
if (null !== $unmatched_action) {
|
|
92
|
+
return [null, $ctx->make_error('point_action_invalid',
|
|
93
|
+
"Operation \"{$op->name}\" action \"" .
|
|
94
|
+
\Voxgig\Struct\Struct::stringify($unmatched_action) .
|
|
95
|
+
"\" is not valid.")];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// A terminal parameter marks a record route (/boards/{id});
|
|
99
|
+
// a cross-reference ends in the relationship's name
|
|
100
|
+
// (/posts/{id}/author). Failing that, the shallower wins.
|
|
101
|
+
$parts_len = function ($p) {
|
|
102
|
+
$parts = \Voxgig\Struct\Struct::getprop($p, 'parts');
|
|
103
|
+
return is_array($parts) ? count($parts) : 0;
|
|
104
|
+
};
|
|
105
|
+
$terminal_param = function ($p) {
|
|
106
|
+
$parts = \Voxgig\Struct\Struct::getprop($p, 'parts');
|
|
107
|
+
if (!is_array($parts) || 0 === count($parts)) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
$last = $parts[count($parts) - 1];
|
|
111
|
+
return is_string($last) && 0 === strpos($last, '{');
|
|
112
|
+
};
|
|
113
|
+
$point = $op->points[0];
|
|
114
|
+
foreach ($op->points as $p) {
|
|
115
|
+
if ($terminal_param($p) !== $terminal_param($point)) {
|
|
116
|
+
if ($terminal_param($p)) {
|
|
117
|
+
$point = $p;
|
|
118
|
+
}
|
|
119
|
+
} elseif ($parts_len($p) < $parts_len($point)) {
|
|
120
|
+
$point = $p;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
77
125
|
if ($reqselector) {
|
|
78
126
|
$req_action = \Voxgig\Struct\Struct::getprop($reqselector, '$action');
|
|
79
127
|
if ($req_action && $point) {
|
|
@@ -5,6 +5,38 @@ from projectname_sdk.utility.voxgig_struct import voxgig_struct as vs
|
|
|
5
5
|
from projectname_sdk.core.helpers import to_map
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
def _parts_len(point):
|
|
9
|
+
parts = vs.getprop(point, "parts")
|
|
10
|
+
return len(parts) if isinstance(parts, list) else 0
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _terminal_param(point):
|
|
14
|
+
# A record route ends in the record's identifier (/boards/{id}); a
|
|
15
|
+
# cross-reference that also returns the entity ends in the
|
|
16
|
+
# relationship's name (/posts/{id}/author).
|
|
17
|
+
parts = vs.getprop(point, "parts")
|
|
18
|
+
if not isinstance(parts, list) or 0 == len(parts):
|
|
19
|
+
return False
|
|
20
|
+
return str(parts[-1]).startswith("{")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _own_point(points):
|
|
24
|
+
# The entity's OWN route: a terminal parameter first, then the fewest
|
|
25
|
+
# path segments. Ties keep the earlier point, so the model's sorted-key
|
|
26
|
+
# order decides. The same rule runs at generation time, in
|
|
27
|
+
# helpers/opShape.ts — both sides must move together.
|
|
28
|
+
best = points[0]
|
|
29
|
+
for cand in points:
|
|
30
|
+
cand_term = _terminal_param(cand)
|
|
31
|
+
best_term = _terminal_param(best)
|
|
32
|
+
if cand_term != best_term:
|
|
33
|
+
if cand_term:
|
|
34
|
+
best = cand
|
|
35
|
+
elif _parts_len(cand) < _parts_len(best):
|
|
36
|
+
best = cand
|
|
37
|
+
return best
|
|
38
|
+
|
|
39
|
+
|
|
8
40
|
def make_point_util(ctx):
|
|
9
41
|
pre = ctx.out.get("point")
|
|
10
42
|
if pre is not None:
|
|
@@ -40,9 +72,10 @@ def make_point_util(ctx):
|
|
|
40
72
|
selector = ctx.match
|
|
41
73
|
|
|
42
74
|
point = None
|
|
75
|
+
matched = False
|
|
43
76
|
for i in range(len(op.points)):
|
|
44
|
-
|
|
45
|
-
select_def = to_map(vs.getprop(
|
|
77
|
+
cand = op.points[i]
|
|
78
|
+
select_def = to_map(vs.getprop(cand, "select"))
|
|
46
79
|
found = True
|
|
47
80
|
|
|
48
81
|
if selector is not None and select_def is not None:
|
|
@@ -63,8 +96,29 @@ def make_point_util(ctx):
|
|
|
63
96
|
found = False
|
|
64
97
|
|
|
65
98
|
if found:
|
|
99
|
+
point = cand
|
|
100
|
+
matched = True
|
|
66
101
|
break
|
|
67
102
|
|
|
103
|
+
# select.exist can list more than the params needed to pick a point,
|
|
104
|
+
# so nothing matches — fall back to the entity's own route rather
|
|
105
|
+
# than whichever point came last.
|
|
106
|
+
if not matched:
|
|
107
|
+
# A request naming an action reaches here only because that
|
|
108
|
+
# action's own point failed its exist test, so it is unbuildable
|
|
109
|
+
# whatever we pick. Refuse it BEFORE choosing a fallback: the
|
|
110
|
+
# guard below compares the chosen point's $action and would wave
|
|
111
|
+
# the request through whenever the fallback lands on the action
|
|
112
|
+
# point itself.
|
|
113
|
+
req_action = vs.getprop(reqselector, "$action") \
|
|
114
|
+
if reqselector is not None else None
|
|
115
|
+
if req_action is not None:
|
|
116
|
+
return None, ctx.make_error("point_action_invalid",
|
|
117
|
+
'Operation "' + op.name + '" action "' +
|
|
118
|
+
vs.stringify(req_action) + '" is not valid.')
|
|
119
|
+
|
|
120
|
+
point = _own_point(op.points)
|
|
121
|
+
|
|
68
122
|
if reqselector is not None:
|
|
69
123
|
req_action = vs.getprop(reqselector, "$action")
|
|
70
124
|
if req_action is not None and point is not None:
|
|
@@ -35,8 +35,8 @@ module ProjectNameUtilities
|
|
|
35
35
|
selector = op.input == "data" ? ctx.data : ctx.match
|
|
36
36
|
|
|
37
37
|
point = nil
|
|
38
|
+
matched = false
|
|
38
39
|
op.points.each do |p|
|
|
39
|
-
point = p
|
|
40
40
|
select_def = ProjectNameHelpers.to_map(VoxgigStruct.getprop(p, "select"))
|
|
41
41
|
found = true
|
|
42
42
|
|
|
@@ -60,7 +60,47 @@ module ProjectNameUtilities
|
|
|
60
60
|
found = false if req_action != select_action
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
if found
|
|
64
|
+
point = p
|
|
65
|
+
matched = true
|
|
66
|
+
break
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# select.exist can list more than the params needed to pick a point, so
|
|
71
|
+
# nothing matches — fall back to the entity's own route rather than
|
|
72
|
+
# whichever point came last.
|
|
73
|
+
unless matched
|
|
74
|
+
# A request naming an action reaches here only because that action's
|
|
75
|
+
# own point failed its exist test, so it is unbuildable whatever we
|
|
76
|
+
# pick. Refuse it BEFORE choosing a fallback: the guard below
|
|
77
|
+
# compares the chosen point's $action and would wave the request
|
|
78
|
+
# through whenever the fallback lands on the action point itself.
|
|
79
|
+
req_action = reqselector ? VoxgigStruct.getprop(reqselector, "$action") : nil
|
|
80
|
+
if req_action
|
|
81
|
+
return nil, ctx.make_error("point_action_invalid",
|
|
82
|
+
"Operation \"#{op.name}\" action \"#{VoxgigStruct.stringify(req_action)}\" is not valid.")
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# A terminal parameter marks a record route (/boards/{id}); a
|
|
86
|
+
# cross-reference ends in the relationship's name
|
|
87
|
+
# (/posts/{id}/author). Failing that, the shallower path wins.
|
|
88
|
+
parts_len = ->(p) {
|
|
89
|
+
parts = VoxgigStruct.getprop(p, "parts")
|
|
90
|
+
parts.is_a?(Array) ? parts.length : 0
|
|
91
|
+
}
|
|
92
|
+
terminal_param = ->(p) {
|
|
93
|
+
parts = VoxgigStruct.getprop(p, "parts")
|
|
94
|
+
parts.is_a?(Array) && parts.length > 0 && parts[-1].to_s.start_with?("{")
|
|
95
|
+
}
|
|
96
|
+
point = op.points[0]
|
|
97
|
+
op.points.each do |p|
|
|
98
|
+
if terminal_param.call(p) != terminal_param.call(point)
|
|
99
|
+
point = p if terminal_param.call(p)
|
|
100
|
+
elsif parts_len.call(p) < parts_len.call(point)
|
|
101
|
+
point = p
|
|
102
|
+
end
|
|
103
|
+
end
|
|
64
104
|
end
|
|
65
105
|
|
|
66
106
|
if reqselector
|