@voxgig/sdkgen 4.2.8 → 4.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/bin/voxgig-sdkgen +1 -1
- package/dist/helpers/naming.d.ts +2 -1
- package/dist/helpers/naming.js +50 -12
- package/dist/helpers/naming.js.map +1 -1
- package/dist/sdkgen.d.ts +2 -2
- package/dist/sdkgen.js +4 -3
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/project/.sdk/src/cmp/zig/Config_zig.ts +1 -1
- package/project/.sdk/tm/c/tests/primary_corpus_test.c +565 -0
- package/project/.sdk/tm/c/utility/prepare_method.c +3 -1
- package/project/.sdk/tm/c/utility/transform_request.c +4 -3
- package/project/.sdk/tm/clojure/src/sdk/core.clj +55 -5
- package/project/.sdk/tm/clojure/test/sdk/test/primary.clj +171 -1
- package/project/.sdk/tm/clojure/test/sdk/test/struct_corpus.clj +13 -2
- package/project/.sdk/tm/clojure/test/sdk/test_runner.clj +2 -0
- package/project/.sdk/tm/csharp/test/CustomUtilityTest.cs +104 -0
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +82 -2
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +50 -2
- package/project/.sdk/tm/elixir/test/primary_utility_test.exs +18 -200
- package/project/.sdk/tm/elixir/test/support/struct_corpus.ex +413 -2
- package/project/.sdk/tm/go/test/custom_utility_test.go +103 -0
- package/project/.sdk/tm/go/test/feature_corpus_test.go +550 -0
- package/project/.sdk/tm/go/utility/make_options.go +7 -1
- package/project/.sdk/tm/go/utility/register.go +194 -0
- package/project/.sdk/tm/java/test/CustomUtilityTest.java +55 -0
- package/project/.sdk/tm/java/test/FeatureCorpusTest.java +463 -0
- package/project/.sdk/tm/java/utility/MakeOptions.java +11 -2
- package/project/.sdk/tm/java/utility/Register.java +91 -0
- package/project/.sdk/tm/js/test/feature/Corpus.test.js +285 -0
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +56 -2
- package/project/.sdk/tm/lua/utility/make_options.lua +22 -2
- package/project/.sdk/tm/ocaml/Makefile +15 -4
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +8 -1
- package/project/.sdk/tm/ocaml/test/corpus_runner.ml +185 -0
- package/project/.sdk/tm/ocaml/test/primary_utility_test.ml +238 -0
- package/project/.sdk/tm/ocaml/test/struct_corpus.ml +5 -160
- package/project/.sdk/tm/perl/t/feature_corpus.t +345 -0
- package/project/.sdk/tm/perl/utility/make_options.pm +33 -1
- package/project/.sdk/tm/php/core/Context.php +3 -0
- package/project/.sdk/tm/php/core/Control.php +13 -0
- package/project/.sdk/tm/php/core/Error.php +12 -0
- package/project/.sdk/tm/php/test/FeatureCorpusTest.php +376 -0
- package/project/.sdk/tm/php/utility/MakeOptions.php +30 -1
- package/project/.sdk/tm/py/pkg/utility/make_options.py +42 -1
- package/project/.sdk/tm/py/test/test_feature_corpus.py +309 -0
- package/project/.sdk/tm/rb/test/feature_corpus_test.rb +281 -0
- package/project/.sdk/tm/rb/utility/make_options.rb +32 -1
- package/project/.sdk/tm/scala/Makefile +1 -0
- package/project/.sdk/tm/scala/sdktest/PrimaryCorpus.scala +294 -0
- package/project/.sdk/tm/scala/sdktest/StructCorpus.scala +0 -0
- package/project/.sdk/tm/scala/utility/Make.scala +49 -2
- package/project/.sdk/tm/scala/utility/Prepare.scala +3 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +21 -3
- package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +13 -2
- package/project/.sdk/tm/ts/test/feature/Corpus.test.ts +287 -0
- package/project/.sdk/tm/zig/core/utility.zig +8 -1
- package/project/.sdk/tm/zig/test/primary_utility_test.zig +445 -0
- package/project/.sdk/tm/zig/test/struct_runner.zig +238 -0
- package/project/sdkgen-package.json +1 -1
- package/src/helpers/naming.ts +54 -12
- package/src/sdkgen.ts +2 -1
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
// Primary-utility corpus driver.
|
|
2
|
+
//
|
|
3
|
+
// Drives every `primary` section of the shared corpus (../.sdk/test/test.json)
|
|
4
|
+
// through this SDK's utilities, the way the ts reference harness does. The
|
|
5
|
+
// match/args/err machinery is reused from Runner (StructCorpus.scala); what
|
|
6
|
+
// this file adds is the bridge between the corpus Value ADT and the SDK's
|
|
7
|
+
// Java-collection world, plus a live Context built from each corpus entry.
|
|
8
|
+
|
|
9
|
+
import voxgig.struct.{clone as sclone, *}
|
|
10
|
+
import SCALAPACKAGE.core.*
|
|
11
|
+
|
|
12
|
+
import java.util.{ArrayList, LinkedHashMap, List as JList, Map as JMap}
|
|
13
|
+
import scala.collection.mutable.{ArrayBuffer, LinkedHashMap as MLinkedHashMap}
|
|
14
|
+
import scala.jdk.CollectionConverters.*
|
|
15
|
+
|
|
16
|
+
object PrimaryCorpus {
|
|
17
|
+
|
|
18
|
+
// ---- Value <-> Java bridge ---------------------------------------------
|
|
19
|
+
|
|
20
|
+
def toJava(v: Value): Object = v match {
|
|
21
|
+
case VMap(m) =>
|
|
22
|
+
val out = new LinkedHashMap[String, Object]()
|
|
23
|
+
m.foreach { case (k, x) => out.put(k, toJava(x)) }
|
|
24
|
+
out
|
|
25
|
+
case VList(b) =>
|
|
26
|
+
val out = new ArrayList[Object]()
|
|
27
|
+
b.foreach { x => out.add(toJava(x)) }
|
|
28
|
+
out
|
|
29
|
+
case VStr(s) => s
|
|
30
|
+
case VBool(b) => java.lang.Boolean.valueOf(b)
|
|
31
|
+
case VNum(n) =>
|
|
32
|
+
// The corpus writes whole numbers as JSON integers; handing the SDK a
|
|
33
|
+
// Double turned "1" into "1.0" everywhere it reached a URL or header.
|
|
34
|
+
if (n == Math.floor(n) && !n.isInfinite) java.lang.Long.valueOf(n.toLong)
|
|
35
|
+
else java.lang.Double.valueOf(n)
|
|
36
|
+
case _ => null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
def toValue(o: Object): Value = o match {
|
|
40
|
+
case null => VNull
|
|
41
|
+
// Struct.UNDEF is the library's "no value" sentinel, not a value: an
|
|
42
|
+
// unresolved transform path returns it, and without this it reached the
|
|
43
|
+
// corpus as the string "java.lang.Object@..." instead of null.
|
|
44
|
+
case x if x eq SCALAPACKAGE.utility.struct.Struct.UNDEF => VNull
|
|
45
|
+
case m: JMap[_, _] =>
|
|
46
|
+
val out = MLinkedHashMap.empty[String, Value]
|
|
47
|
+
m.asInstanceOf[JMap[String, Object]].asScala.foreach { case (k, x) => out.put(k, toValue(x)) }
|
|
48
|
+
VMap(out)
|
|
49
|
+
case l: JList[_] =>
|
|
50
|
+
val out = ArrayBuffer.empty[Value]
|
|
51
|
+
l.asInstanceOf[JList[Object]].asScala.foreach { x => out += toValue(x) }
|
|
52
|
+
VList(out)
|
|
53
|
+
case s: String => VStr(s)
|
|
54
|
+
case b: java.lang.Boolean => VBool(b.booleanValue())
|
|
55
|
+
case n: java.lang.Number => VNum(n.doubleValue())
|
|
56
|
+
case e: RuntimeException =>
|
|
57
|
+
val m = MLinkedHashMap.empty[String, Value]
|
|
58
|
+
m.put("message", VStr(String.valueOf(e.getMessage)))
|
|
59
|
+
VMap(m)
|
|
60
|
+
case x => VStr(String.valueOf(x))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private def jmap(v: Value): JMap[String, Object] = toJava(v) match {
|
|
64
|
+
case m: JMap[_, _] => m.asInstanceOf[JMap[String, Object]]
|
|
65
|
+
case _ => new LinkedHashMap[String, Object]()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private def vget(v: Value, k: String): Value = v match {
|
|
69
|
+
case VMap(m) => m.getOrElse(k, Noval)
|
|
70
|
+
case _ => Noval
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ---- corpus access -----------------------------------------------------
|
|
74
|
+
|
|
75
|
+
private var CORPUS: Value = Noval
|
|
76
|
+
|
|
77
|
+
private def sectionBasic(name: String): Value = vget(vget(vget(CORPUS, "primary"), name), "basic")
|
|
78
|
+
|
|
79
|
+
// makeSpec and prepareAuth read defaults off the CLIENT, as the ts reference
|
|
80
|
+
// does via client.options(), so a section's DEF.setup cannot reach them
|
|
81
|
+
// through ctx.options — those sections get their own client.
|
|
82
|
+
private def sectionSetup(name: String): Value =
|
|
83
|
+
vget(vget(vget(vget(vget(CORPUS, "primary"), name), "DEF"), "setup"), "a")
|
|
84
|
+
|
|
85
|
+
// ---- live context from a corpus map ------------------------------------
|
|
86
|
+
|
|
87
|
+
private def corpusCtx(client: ProjectNameSDK, ctxmap: Value): Context = {
|
|
88
|
+
val utility = client.getUtility()
|
|
89
|
+
val cm = new LinkedHashMap[String, Object]()
|
|
90
|
+
// Only when the corpus names one: defaulting to "load" made the SDK report
|
|
91
|
+
// the wrong operation in the error messages the corpus matches on.
|
|
92
|
+
vget(ctxmap, "opname") match {
|
|
93
|
+
case VStr(s) => cm.put("opname", s)
|
|
94
|
+
case _ =>
|
|
95
|
+
}
|
|
96
|
+
val ctx = utility.makeContext(cm, client.getRootCtx())
|
|
97
|
+
|
|
98
|
+
vget(ctxmap, "spec") match {
|
|
99
|
+
case m @ VMap(_) => ctx.spec = new Spec(jmap(m))
|
|
100
|
+
case _ =>
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
vget(ctxmap, "result") match {
|
|
104
|
+
case m @ VMap(_) =>
|
|
105
|
+
val r = new Result(jmap(m))
|
|
106
|
+
// Result does not carry an err in from the map, so a corpus result
|
|
107
|
+
// holding one arrived empty and resultBasic had no previous message
|
|
108
|
+
// to prepend.
|
|
109
|
+
vget(vget(m, "err"), "message") match {
|
|
110
|
+
case VStr(msg) if msg.nonEmpty => r.err = new SdkError("", msg, null)
|
|
111
|
+
case _ =>
|
|
112
|
+
}
|
|
113
|
+
ctx.result = r
|
|
114
|
+
case _ =>
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
vget(ctxmap, "response") match {
|
|
118
|
+
case m @ VMap(_) =>
|
|
119
|
+
val resp = new Response(jmap(m))
|
|
120
|
+
// resultBody reads response.json and requires it to be CALLABLE; the
|
|
121
|
+
// corpus supplies a plain `body`.
|
|
122
|
+
vget(m, "body") match {
|
|
123
|
+
case Noval => ()
|
|
124
|
+
case b =>
|
|
125
|
+
val bj = toJava(b)
|
|
126
|
+
resp.body = bj
|
|
127
|
+
resp.jsonFunc = new java.util.function.Supplier[Object] { def get(): Object = bj }
|
|
128
|
+
}
|
|
129
|
+
// Header names arrive from the wire in any case and the contract is
|
|
130
|
+
// lowercase; resultHeaders copies them verbatim.
|
|
131
|
+
vget(m, "headers") match {
|
|
132
|
+
case VMap(hm) =>
|
|
133
|
+
val low = new LinkedHashMap[String, Object]()
|
|
134
|
+
hm.foreach { case (k, x) => low.put(k.toLowerCase, toJava(x)) }
|
|
135
|
+
resp.headers = low
|
|
136
|
+
case _ =>
|
|
137
|
+
}
|
|
138
|
+
ctx.response = resp
|
|
139
|
+
case _ =>
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
vget(ctxmap, "point") match { case m @ VMap(_) => ctx.point = jmap(m); case _ => }
|
|
143
|
+
vget(ctxmap, "reqdata") match { case Noval => (); case v => ctx.reqdata = jmap(v) }
|
|
144
|
+
vget(ctxmap, "reqmatch") match { case Noval => (); case v => ctx.reqmatch = jmap(v) }
|
|
145
|
+
vget(ctxmap, "data") match { case Noval => (); case v => ctx.data = jmap(v) }
|
|
146
|
+
vget(ctxmap, "match") match { case Noval => (); case v => ctx.matchData = jmap(v) }
|
|
147
|
+
vget(ctxmap, "options") match { case m @ VMap(_) => ctx.options = jmap(m); case _ => }
|
|
148
|
+
vget(ctxmap, "config") match { case m @ VMap(_) => ctx.config = jmap(m); case _ => }
|
|
149
|
+
ctx
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// The match reads the corpus map while the utilities mutate the live objects
|
|
153
|
+
// hanging off the context, so without this every ctx.* assertion reads null.
|
|
154
|
+
private def publishCtx(ctxmap: Value, ctx: Context): Unit = ctxmap match {
|
|
155
|
+
case VMap(m) =>
|
|
156
|
+
if (ctx.spec != null) m.put("spec", specValue(ctx.spec))
|
|
157
|
+
if (ctx.result != null) m.put("result", resultValue(ctx.result))
|
|
158
|
+
if (ctx.response != null) m.put("response", VStr(Runner.EXISTSMARK))
|
|
159
|
+
case _ =>
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private def specValue(s: Spec): Value = {
|
|
163
|
+
val m = MLinkedHashMap.empty[String, Value]
|
|
164
|
+
m.put("base", VStr(s.base)); m.put("prefix", VStr(s.prefix)); m.put("suffix", VStr(s.suffix))
|
|
165
|
+
m.put("path", VStr(s.path)); m.put("url", VStr(s.url)); m.put("step", VStr(s.step))
|
|
166
|
+
m.put("method", VStr(s.method))
|
|
167
|
+
m.put("headers", toValue(s.headers)); m.put("params", toValue(s.params))
|
|
168
|
+
m.put("query", toValue(s.query)); m.put("alias", toValue(s.alias))
|
|
169
|
+
if (s.parts != null) m.put("parts", toValue(s.parts))
|
|
170
|
+
if (s.body != null) m.put("body", toValue(s.body))
|
|
171
|
+
VMap(m)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private def resultValue(r: Result): Value = {
|
|
175
|
+
val m = MLinkedHashMap.empty[String, Value]
|
|
176
|
+
m.put("ok", VBool(r.ok)); m.put("status", VNum(r.status.toDouble))
|
|
177
|
+
m.put("statusText", VStr(r.statusText))
|
|
178
|
+
m.put("headers", toValue(r.headers))
|
|
179
|
+
if (r.body != null) m.put("body", toValue(r.body))
|
|
180
|
+
if (r.resdata != null) m.put("resdata", toValue(r.resdata))
|
|
181
|
+
if (r.resmatch != null) m.put("resmatch", toValue(r.resmatch))
|
|
182
|
+
if (r.err != null) {
|
|
183
|
+
val e = MLinkedHashMap.empty[String, Value]
|
|
184
|
+
e.put("message", VStr(String.valueOf(r.err.getMessage)))
|
|
185
|
+
m.put("err", VMap(e))
|
|
186
|
+
}
|
|
187
|
+
VMap(m)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ---- section runners ---------------------------------------------------
|
|
191
|
+
|
|
192
|
+
private def argAt(args: Seq[Value], i: Int): Value = if (i < args.length) args(i) else Noval
|
|
193
|
+
|
|
194
|
+
// A section driven with a ctx built from the corpus entry.
|
|
195
|
+
private def runset(name: String, client: ProjectNameSDK)(f: (Context, Seq[Value]) => Value): Unit =
|
|
196
|
+
Runner.runSet(name, sectionBasic(name), (args: Seq[Value]) => {
|
|
197
|
+
val ctxmap = argAt(args, 0)
|
|
198
|
+
val ctx = corpusCtx(client, ctxmap)
|
|
199
|
+
val out = f(ctx, args)
|
|
200
|
+
publishCtx(ctxmap, ctx)
|
|
201
|
+
out
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
// A section that takes a bare map or explicit args rather than a ctx.
|
|
205
|
+
private def runsetArgs(name: String)(f: Seq[Value] => Value): Unit =
|
|
206
|
+
Runner.runSet(name, sectionBasic(name), f)
|
|
207
|
+
|
|
208
|
+
private def clientFor(name: String, shared: ProjectNameSDK): ProjectNameSDK =
|
|
209
|
+
sectionSetup(name) match {
|
|
210
|
+
case m @ VMap(_) => ProjectNameSDK.testSDK(null, jmap(m))
|
|
211
|
+
case _ => shared
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
def run(testfile: String): Int = {
|
|
215
|
+
val src = new String(java.nio.file.Files.readAllBytes(java.nio.file.Paths.get(testfile)), "UTF-8")
|
|
216
|
+
CORPUS = Runner.jsonRead(src)
|
|
217
|
+
|
|
218
|
+
val sdk = ProjectNameSDK.testSDK()
|
|
219
|
+
val u = sdk.getUtility()
|
|
220
|
+
|
|
221
|
+
runset("done", sdk) { (c, _) => toValue(u.done(c)) }
|
|
222
|
+
runset("makeUrl", sdk) { (c, _) => toValue(u.makeUrl(c)) }
|
|
223
|
+
runset("makeRequest", sdk) { (c, _) => u.makeRequest(c); resultValue(c.result) }
|
|
224
|
+
runset("makeResponse", sdk) { (c, _) => u.makeResponse(c); resultValue(c.result) }
|
|
225
|
+
runset("makeSpec", clientFor("makeSpec", sdk)) { (c, _) =>
|
|
226
|
+
val s = u.makeSpec(c); if (s != null) c.spec = s; specValue(c.spec)
|
|
227
|
+
}
|
|
228
|
+
runset("prepareAuth", clientFor("prepareAuth", sdk)) { (c, _) =>
|
|
229
|
+
u.prepareAuth(c); specValue(c.spec)
|
|
230
|
+
}
|
|
231
|
+
runset("prepareBody", sdk) { (c, _) => toValue(u.prepareBody(c)) }
|
|
232
|
+
runset("prepareHeaders", sdk) { (c, _) => toValue(u.prepareHeaders(c)) }
|
|
233
|
+
runset("prepareMethod", sdk) { (c, _) =>
|
|
234
|
+
val m = u.prepareMethod(c); if (m == null || m.isEmpty) VNull else VStr(m)
|
|
235
|
+
}
|
|
236
|
+
runset("prepareParams", sdk) { (c, _) => toValue(u.prepareParams(c)) }
|
|
237
|
+
runset("preparePath", sdk) { (c, _) => toValue(u.preparePath(c)) }
|
|
238
|
+
runset("prepareQuery", sdk) { (c, _) => toValue(u.prepareQuery(c)) }
|
|
239
|
+
runset("resultBasic", sdk) { (c, _) =>
|
|
240
|
+
val r = u.resultBasic(c); if (r != null) c.result = r; resultValue(c.result)
|
|
241
|
+
}
|
|
242
|
+
runset("resultBody", sdk) { (c, _) =>
|
|
243
|
+
val r = u.resultBody(c); if (r != null) c.result = r; resultValue(c.result)
|
|
244
|
+
}
|
|
245
|
+
runset("resultHeaders", sdk) { (c, _) =>
|
|
246
|
+
val r = u.resultHeaders(c); if (r != null) c.result = r; resultValue(c.result)
|
|
247
|
+
}
|
|
248
|
+
runset("transformRequest", sdk) { (c, _) => toValue(u.transformRequest(c)) }
|
|
249
|
+
runset("transformResponse", sdk) { (c, _) => toValue(u.transformResponse(c)) }
|
|
250
|
+
runset("param", sdk) { (c, args) => toValue(u.param(c, toJava(argAt(args, 1)))) }
|
|
251
|
+
runset("makeError", sdk) { (c, args) =>
|
|
252
|
+
val msg = vget(argAt(args, 1), "message") match { case VStr(s) => s; case _ => "" }
|
|
253
|
+
val in = if (msg.isEmpty) null else new SdkError("", msg, null)
|
|
254
|
+
toValue(u.makeError(c, in))
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
runsetArgs("makeContext") { args =>
|
|
258
|
+
val c = corpusCtx(sdk, argAt(args, 0))
|
|
259
|
+
val op = MLinkedHashMap.empty[String, Value]
|
|
260
|
+
if (c.op != null) {
|
|
261
|
+
op.put("entity", VStr(c.op.entity)); op.put("name", VStr(c.op.name))
|
|
262
|
+
op.put("input", VStr(c.op.input)); op.put("points", toValue(c.op.points))
|
|
263
|
+
}
|
|
264
|
+
val out = MLinkedHashMap.empty[String, Value]
|
|
265
|
+
out.put("op", VMap(op))
|
|
266
|
+
VMap(out)
|
|
267
|
+
}
|
|
268
|
+
runsetArgs("makeOptions") { args =>
|
|
269
|
+
val in = argAt(args, 0)
|
|
270
|
+
val c = corpusCtx(sdk, VMap(MLinkedHashMap.empty[String, Value]))
|
|
271
|
+
vget(in, "config") match { case Noval => (); case v => c.config = jmap(v) }
|
|
272
|
+
vget(in, "options") match { case Noval => (); case v => c.options = jmap(v) }
|
|
273
|
+
toValue(u.makeOptions(c))
|
|
274
|
+
}
|
|
275
|
+
runsetArgs("operator") { args =>
|
|
276
|
+
val in = argAt(args, 0)
|
|
277
|
+
val m = MLinkedHashMap.empty[String, Value]
|
|
278
|
+
m.put("entity", vget(in, "entity") match { case Noval => VStr("_"); case v => v })
|
|
279
|
+
m.put("input", vget(in, "input") match { case Noval => VStr("_"); case v => v })
|
|
280
|
+
m.put("name", vget(in, "name") match { case Noval => VStr("_"); case v => v })
|
|
281
|
+
m.put("points", vget(in, "points") match { case l @ VList(_) => l; case _ => VList(ArrayBuffer.empty[Value]) })
|
|
282
|
+
VMap(m)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
Runner.reportPrimary()
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
object PrimaryCorpusMain {
|
|
290
|
+
def main(args: Array[String]): Unit = {
|
|
291
|
+
val testfile = if (args.length > 0) args(0) else "../.sdk/test/test.json"
|
|
292
|
+
System.exit(PrimaryCorpus.run(testfile))
|
|
293
|
+
}
|
|
294
|
+
}
|
|
Binary file
|
|
@@ -199,13 +199,60 @@ object MakePoint {
|
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
object MakeOptions {
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Replaces one utility member from `options.utility`, matching the ts
|
|
205
|
+
* reference: a key naming a real member REPLACES it, and any other key is
|
|
206
|
+
* attached as a custom extra. Returns false when the key names no member or
|
|
207
|
+
* the value is not that member's type, so the caller keeps it in `custom`.
|
|
208
|
+
*
|
|
209
|
+
* REFLECTION, NOT A KEYED SWITCH. The go and java ports list every member by
|
|
210
|
+
* hand and carry a "keep this in step with registerAll" warning, because a
|
|
211
|
+
* utility added to one list and not the other is overridable there and not
|
|
212
|
+
* here. The field set is readable off the class, so the list cannot drift.
|
|
213
|
+
*
|
|
214
|
+
* Scala `var` fields compile to a private field plus accessors, so the
|
|
215
|
+
* lookup is on the declared field and needs setAccessible. Function types
|
|
216
|
+
* erase on the JVM, so isInstance checks the FunctionN arity rather than the
|
|
217
|
+
* full signature - the same limit java's port documents.
|
|
218
|
+
*
|
|
219
|
+
* Only a PUBLIC name may replace a member: public utility names are
|
|
220
|
+
* camelCase and carry no underscore, so an underscore means the caller named
|
|
221
|
+
* something of their own rather than a member.
|
|
222
|
+
*/
|
|
223
|
+
def overrideUtil(utility: Utility, key: String, value: Object): Boolean = {
|
|
224
|
+
if (null == key || key.isEmpty || key.contains("_") || "custom" == key) return false
|
|
225
|
+
if (null == value) return false
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
val field = classOf[Utility].getDeclaredField(key)
|
|
229
|
+
if (!field.getType.isInstance(value)) return false
|
|
230
|
+
field.setAccessible(true)
|
|
231
|
+
field.set(utility, value)
|
|
232
|
+
true
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
case _: NoSuchFieldException => false
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
202
239
|
def makeOptions(ctx: Context): JMap[String, Object] = {
|
|
203
240
|
var options = ctx.options
|
|
204
241
|
if (options == null) options = new LinkedHashMap[String, Object]()
|
|
205
242
|
|
|
206
|
-
// Merge
|
|
243
|
+
// Merge utility overrides from options onto the utility object.
|
|
244
|
+
//
|
|
245
|
+
// A key naming a real utility member REPLACES it; anything else is
|
|
246
|
+
// attached as a custom extra. Shelving everything in `custom` - a map
|
|
247
|
+
// nothing reads - made `utility -> Map("fetcher" -> ...)`, the documented
|
|
248
|
+
// transport seam, a silent no-op here while ts honoured it.
|
|
207
249
|
val customUtils = Helpers.toMapAny(options.get("utility"))
|
|
208
|
-
if (customUtils != null && ctx.utility != null)
|
|
250
|
+
if (customUtils != null && ctx.utility != null) {
|
|
251
|
+
customUtils.entrySet().forEach { e =>
|
|
252
|
+
if (!overrideUtil(ctx.utility, e.getKey, e.getValue))
|
|
253
|
+
ctx.utility.custom.put(e.getKey, e.getValue)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
209
256
|
|
|
210
257
|
var opts = Struct.clone(options).asInstanceOf[JMap[String, Object]]
|
|
211
258
|
|
|
@@ -26,7 +26,9 @@ object PrepareMethod {
|
|
|
26
26
|
case "list" => "GET"
|
|
27
27
|
case "remove" => "DELETE"
|
|
28
28
|
case "patch" => "PATCH"
|
|
29
|
-
|
|
29
|
+
// No GET catch-all: an unrecognised op must fall through to the
|
|
30
|
+
// allow.method gate, not be silently issued as a GET.
|
|
31
|
+
case _ => ""
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
34
|
}
|
|
@@ -57,12 +57,30 @@ private func buildOptSpec() -> Value {
|
|
|
57
57
|
func makeOptionsUtil(_ ctx: Context) -> VMap {
|
|
58
58
|
let options = ctx.options ?? VMap()
|
|
59
59
|
|
|
60
|
-
// Merge
|
|
61
|
-
// original options before clone for safety.
|
|
60
|
+
// Merge utility overrides from options onto the utility object. Read from
|
|
61
|
+
// the original options before clone for safety.
|
|
62
|
+
//
|
|
63
|
+
// `fetcher` REPLACES the member; every other key is attached as a custom
|
|
64
|
+
// extra. Shelving the fetcher too made `utility: ["fetcher": ...]`, the
|
|
65
|
+
// documented transport seam, a silent no-op here while ts honoured it - and
|
|
66
|
+
// it is the seam the shared feature corpus scripts.
|
|
67
|
+
//
|
|
68
|
+
// PARTIAL, DELIBERATELY. The other members are replaceable in principle -
|
|
69
|
+
// Value.native carries any Swift value - but each has a distinct closure
|
|
70
|
+
// type, so honouring them means one `as?` arm per member with the signature
|
|
71
|
+
// written exactly. `fetcher` is the documented seam and the one the corpus
|
|
72
|
+
// needs; the rest stay in `custom` until a swift toolchain can compile the
|
|
73
|
+
// arms rather than have them written blind. See AGENTS.md.
|
|
62
74
|
if let customUtils = gp(options, "utility").asMap {
|
|
63
75
|
if let utility = ctx.utility {
|
|
64
76
|
for (k, v) in customUtils.entries {
|
|
65
|
-
|
|
77
|
+
let native = v.asNative ?? v
|
|
78
|
+
if "fetcher" == k, let fn = native as? FetcherFunc {
|
|
79
|
+
utility.fetcher = fn
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
utility.custom[k] = native
|
|
83
|
+
}
|
|
66
84
|
}
|
|
67
85
|
}
|
|
68
86
|
}
|
|
@@ -30,8 +30,19 @@ function ownIdField(config: any, getpath: any, entityName: string): string {
|
|
|
30
30
|
if (ptterm !== bestterm ? ptterm : pt.parts.length < best.parts.length) best = pt
|
|
31
31
|
}
|
|
32
32
|
const parts: string[] = (best && best.parts) || []
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
// THE LAST PART, not the last param anywhere in the path. A record route
|
|
34
|
+
// ENDS in its key: /orgs/{org}/private-registries/{secret_name} does,
|
|
35
|
+
// /orgs/{org}/private-registries/public-key does not. Reading the last
|
|
36
|
+
// param wherever it fell returned `{org}` for that second path — a PARENT
|
|
37
|
+
// reference — and the seeding walk then stamped the record's own key over
|
|
38
|
+
// org_id, destroying the ORG01 the fixture set and the test looks up from
|
|
39
|
+
// idmap. github's private_registry failed its update with a 404 that named
|
|
40
|
+
// nothing to do with orgs.
|
|
41
|
+
//
|
|
42
|
+
// A point that does not end in a param says nothing about this entity's
|
|
43
|
+
// key, so move on to the next op rather than guess from it.
|
|
44
|
+
const lastPart = 0 < parts.length ? String(parts[parts.length - 1]) : ''
|
|
45
|
+
if (lastPart.startsWith('{')) return lastPart.slice(1, -1)
|
|
35
46
|
}
|
|
36
47
|
return 'id'
|
|
37
48
|
}
|