@voxgig/sdkgen 4.1.0 → 4.2.1
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/canonType.d.ts +2 -1
- package/dist/helpers/canonType.js +36 -0
- package/dist/helpers/canonType.js.map +1 -1
- package/dist/sdkgen.d.ts +2 -2
- package/dist/sdkgen.js +3 -2
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/project/.sdk/src/cmp/c/ReadmeEntity_c.ts +2 -2
- package/project/.sdk/src/cmp/c/ReadmeHowto_c.ts +2 -2
- package/project/.sdk/src/cmp/c/ReadmeQuick_c.ts +2 -2
- package/project/.sdk/src/cmp/c/ReadmeRef_c.ts +2 -2
- package/project/.sdk/src/cmp/c/ReadmeTopQuick_c.ts +2 -2
- package/project/.sdk/src/cmp/c/ReadmeTopTest_c.ts +2 -2
- package/project/.sdk/src/cmp/clojure/ReadmeEntity_clojure.ts +2 -2
- package/project/.sdk/src/cmp/clojure/ReadmeHowto_clojure.ts +2 -2
- package/project/.sdk/src/cmp/clojure/ReadmeQuick_clojure.ts +2 -2
- package/project/.sdk/src/cmp/clojure/ReadmeRef_clojure.ts +2 -2
- package/project/.sdk/src/cmp/clojure/ReadmeTopQuick_clojure.ts +2 -2
- package/project/.sdk/src/cmp/clojure/ReadmeTopTest_clojure.ts +2 -2
- package/project/.sdk/src/cmp/cpp/ReadmeEntity_cpp.ts +2 -2
- package/project/.sdk/src/cmp/cpp/ReadmeHowto_cpp.ts +2 -2
- package/project/.sdk/src/cmp/cpp/ReadmeQuick_cpp.ts +2 -2
- package/project/.sdk/src/cmp/cpp/ReadmeRef_cpp.ts +2 -2
- package/project/.sdk/src/cmp/cpp/ReadmeTopQuick_cpp.ts +2 -2
- package/project/.sdk/src/cmp/cpp/ReadmeTopTest_cpp.ts +2 -2
- package/project/.sdk/src/cmp/csharp/ReadmeEntity_csharp.ts +2 -2
- package/project/.sdk/src/cmp/csharp/ReadmeHowto_csharp.ts +2 -2
- package/project/.sdk/src/cmp/csharp/ReadmeQuick_csharp.ts +2 -2
- package/project/.sdk/src/cmp/csharp/ReadmeRef_csharp.ts +2 -2
- package/project/.sdk/src/cmp/csharp/ReadmeTopQuick_csharp.ts +2 -2
- package/project/.sdk/src/cmp/csharp/ReadmeTopTest_csharp.ts +2 -2
- package/project/.sdk/src/cmp/dart/ReadmeEntity_dart.ts +2 -2
- package/project/.sdk/src/cmp/dart/ReadmeHowto_dart.ts +2 -2
- package/project/.sdk/src/cmp/dart/ReadmeQuick_dart.ts +2 -2
- package/project/.sdk/src/cmp/dart/ReadmeRef_dart.ts +2 -2
- package/project/.sdk/src/cmp/dart/ReadmeTopQuick_dart.ts +2 -2
- package/project/.sdk/src/cmp/dart/ReadmeTopTest_dart.ts +2 -2
- package/project/.sdk/src/cmp/elixir/utility_elixir.ts +2 -1
- package/project/.sdk/src/cmp/go/ReadmeHowto_go.ts +2 -2
- package/project/.sdk/src/cmp/go/ReadmeTopTest_go.ts +2 -2
- package/project/.sdk/src/cmp/go/utility_go.ts +10 -1
- package/project/.sdk/src/cmp/java/ReadmeEntity_java.ts +2 -2
- package/project/.sdk/src/cmp/java/ReadmeHowto_java.ts +2 -2
- package/project/.sdk/src/cmp/java/ReadmeQuick_java.ts +2 -2
- package/project/.sdk/src/cmp/java/ReadmeRef_java.ts +2 -2
- package/project/.sdk/src/cmp/java/ReadmeTopQuick_java.ts +2 -2
- package/project/.sdk/src/cmp/java/ReadmeTopTest_java.ts +2 -2
- package/project/.sdk/src/cmp/js/utility_js.ts +10 -1
- package/project/.sdk/src/cmp/kotlin/ReadmeEntity_kotlin.ts +2 -2
- package/project/.sdk/src/cmp/kotlin/ReadmeHowto_kotlin.ts +2 -2
- package/project/.sdk/src/cmp/kotlin/ReadmeQuick_kotlin.ts +2 -2
- package/project/.sdk/src/cmp/kotlin/ReadmeRef_kotlin.ts +2 -2
- package/project/.sdk/src/cmp/kotlin/ReadmeTopQuick_kotlin.ts +2 -2
- package/project/.sdk/src/cmp/kotlin/ReadmeTopTest_kotlin.ts +2 -2
- package/project/.sdk/src/cmp/lua/ReadmeEntity_lua.ts +2 -2
- package/project/.sdk/src/cmp/lua/ReadmeHowto_lua.ts +2 -2
- package/project/.sdk/src/cmp/lua/ReadmeQuick_lua.ts +2 -2
- package/project/.sdk/src/cmp/lua/ReadmeRef_lua.ts +2 -2
- package/project/.sdk/src/cmp/lua/ReadmeTopQuick_lua.ts +2 -2
- package/project/.sdk/src/cmp/lua/ReadmeTopTest_lua.ts +2 -2
- package/project/.sdk/src/cmp/ocaml/ReadmeEntity_ocaml.ts +2 -2
- package/project/.sdk/src/cmp/ocaml/ReadmeHowto_ocaml.ts +2 -2
- package/project/.sdk/src/cmp/ocaml/ReadmeQuick_ocaml.ts +2 -2
- package/project/.sdk/src/cmp/ocaml/ReadmeRef_ocaml.ts +2 -2
- package/project/.sdk/src/cmp/ocaml/ReadmeTopQuick_ocaml.ts +2 -2
- package/project/.sdk/src/cmp/ocaml/ReadmeTopTest_ocaml.ts +2 -2
- package/project/.sdk/src/cmp/perl/ReadmeEntity_perl.ts +2 -2
- package/project/.sdk/src/cmp/perl/ReadmeHowto_perl.ts +2 -2
- package/project/.sdk/src/cmp/perl/ReadmeQuick_perl.ts +2 -2
- package/project/.sdk/src/cmp/perl/ReadmeRef_perl.ts +2 -2
- package/project/.sdk/src/cmp/perl/ReadmeTopQuick_perl.ts +2 -2
- package/project/.sdk/src/cmp/perl/ReadmeTopTest_perl.ts +2 -2
- package/project/.sdk/src/cmp/php/ReadmeEntity_php.ts +2 -2
- package/project/.sdk/src/cmp/php/ReadmeHowto_php.ts +2 -2
- package/project/.sdk/src/cmp/php/ReadmeQuick_php.ts +2 -2
- package/project/.sdk/src/cmp/php/ReadmeRef_php.ts +2 -2
- package/project/.sdk/src/cmp/php/ReadmeTopQuick_php.ts +2 -2
- package/project/.sdk/src/cmp/php/ReadmeTopTest_php.ts +2 -2
- package/project/.sdk/src/cmp/py/ReadmeEntity_py.ts +2 -2
- package/project/.sdk/src/cmp/py/ReadmeHowto_py.ts +2 -2
- package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +2 -2
- package/project/.sdk/src/cmp/py/ReadmeRef_py.ts +2 -2
- package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +2 -2
- package/project/.sdk/src/cmp/py/ReadmeTopTest_py.ts +2 -2
- package/project/.sdk/src/cmp/rb/ReadmeEntity_rb.ts +2 -2
- package/project/.sdk/src/cmp/rb/ReadmeHowto_rb.ts +2 -2
- package/project/.sdk/src/cmp/rb/ReadmeQuick_rb.ts +2 -2
- package/project/.sdk/src/cmp/rb/ReadmeRef_rb.ts +2 -2
- package/project/.sdk/src/cmp/rb/ReadmeTopQuick_rb.ts +2 -2
- package/project/.sdk/src/cmp/rb/ReadmeTopTest_rb.ts +2 -2
- package/project/.sdk/src/cmp/rust/ReadmeEntity_rust.ts +2 -2
- package/project/.sdk/src/cmp/rust/ReadmeHowto_rust.ts +2 -2
- package/project/.sdk/src/cmp/rust/ReadmeQuick_rust.ts +2 -2
- package/project/.sdk/src/cmp/rust/ReadmeRef_rust.ts +2 -2
- package/project/.sdk/src/cmp/rust/ReadmeTopQuick_rust.ts +2 -2
- package/project/.sdk/src/cmp/rust/ReadmeTopTest_rust.ts +2 -2
- package/project/.sdk/src/cmp/scala/ReadmeEntity_scala.ts +2 -2
- package/project/.sdk/src/cmp/scala/ReadmeHowto_scala.ts +2 -2
- package/project/.sdk/src/cmp/scala/ReadmeQuick_scala.ts +2 -2
- package/project/.sdk/src/cmp/scala/ReadmeRef_scala.ts +2 -2
- package/project/.sdk/src/cmp/scala/ReadmeTopQuick_scala.ts +2 -2
- package/project/.sdk/src/cmp/scala/ReadmeTopTest_scala.ts +2 -2
- package/project/.sdk/src/cmp/scala/TestDirect_scala.ts +1 -1
- package/project/.sdk/src/cmp/scala/TestEntity_scala.ts +1 -1
- package/project/.sdk/src/cmp/swift/ReadmeEntity_swift.ts +2 -2
- package/project/.sdk/src/cmp/swift/ReadmeHowto_swift.ts +2 -2
- package/project/.sdk/src/cmp/swift/ReadmeQuick_swift.ts +2 -2
- package/project/.sdk/src/cmp/swift/ReadmeRef_swift.ts +2 -2
- package/project/.sdk/src/cmp/swift/ReadmeTopQuick_swift.ts +2 -2
- package/project/.sdk/src/cmp/swift/ReadmeTopTest_swift.ts +2 -2
- package/project/.sdk/src/cmp/ts/utility_ts.ts +10 -1
- package/project/.sdk/src/cmp/zig/ReadmeEntity_zig.ts +2 -2
- package/project/.sdk/src/cmp/zig/ReadmeHowto_zig.ts +2 -2
- package/project/.sdk/src/cmp/zig/ReadmeQuick_zig.ts +2 -2
- package/project/.sdk/src/cmp/zig/ReadmeRef_zig.ts +2 -2
- package/project/.sdk/src/cmp/zig/ReadmeTopQuick_zig.ts +2 -2
- package/project/.sdk/src/cmp/zig/ReadmeTopTest_zig.ts +2 -2
- package/project/.sdk/tm/clojure/src/sdk/features.clj +21 -3
- package/project/.sdk/tm/cpp/Makefile +11 -1
- package/project/.sdk/tm/dart/lib/feature/test/TestFeature.dart +38 -10
- package/project/.sdk/tm/elixir/lib/projectname/feature/test.ex +1 -1
- package/project/.sdk/tm/lean/src/SdkRuntime.lean +19 -1
- package/project/.sdk/tm/lean/src/SdkUtility.lean +2 -2
- package/project/.sdk/tm/ocaml/sdk_features.ml +31 -12
- package/project/.sdk/tm/py-data/test/conftest.py +24 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/TestFeature.swift +10 -10
- package/project/sdkgen-package.json +1 -1
- package/src/helpers/canonType.ts +38 -0
- package/src/sdkgen.ts +2 -1
|
@@ -38,12 +38,40 @@ class TestFeature extends BaseFeature {
|
|
|
38
38
|
|
|
39
39
|
final self = this;
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
// THE MOCK HAS TO AGREE WITH THE MODEL. A point carrying
|
|
42
|
+
// `transform.res: `body.item`` describes an API that answers
|
|
43
|
+
// {"item": {...}}, and the response transform unwraps that key on the way
|
|
44
|
+
// back. Returning the bare payload means the transform unwraps a property
|
|
45
|
+
// that is not there and the caller gets nothing. Mirrors the go/ts/lua/php
|
|
46
|
+
// mocks.
|
|
47
|
+
dynamic envelope(dynamic fctx, dynamic data) {
|
|
48
|
+
if (null == data || null == fctx || null == fctx.point) {
|
|
49
|
+
return data;
|
|
50
|
+
}
|
|
51
|
+
// dart wraps the point in a Point OBJECT (Context._aspoint), so its
|
|
52
|
+
// fields are typed properties, not map keys — vs.getprop on it returns
|
|
53
|
+
// null and the envelope silently never applies.
|
|
54
|
+
final tm = fctx.point.transform;
|
|
55
|
+
final spec = vs.getprop(tm, 'res');
|
|
56
|
+
if (spec is! String) {
|
|
57
|
+
return data;
|
|
58
|
+
}
|
|
59
|
+
// Exactly `body.<key>`; a deeper path is not an envelope this mock can
|
|
60
|
+
// synthesise, so it is left alone rather than guessed at.
|
|
61
|
+
final m = RegExp(r'^`body\.([^`.]+)`$').firstMatch(spec);
|
|
62
|
+
if (null == m) {
|
|
63
|
+
return data;
|
|
64
|
+
}
|
|
65
|
+
return <String, dynamic>{m.group(1)!: data};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
dynamic respond(dynamic fctx, int status, [dynamic data, dynamic res]) {
|
|
69
|
+
final payload = envelope(fctx, data);
|
|
42
70
|
final out = vs.merge([
|
|
43
71
|
<String, dynamic>{
|
|
44
72
|
'status': status,
|
|
45
73
|
'statusText': 'OK',
|
|
46
|
-
'json': () =>
|
|
74
|
+
'json': () => payload,
|
|
47
75
|
'body': 'not-used',
|
|
48
76
|
},
|
|
49
77
|
vs.getdef(res, {}),
|
|
@@ -66,30 +94,30 @@ class TestFeature extends BaseFeature {
|
|
|
66
94
|
final found = vs.select(entmap, args);
|
|
67
95
|
final ent = vs.getelem(found, 0);
|
|
68
96
|
if (null == ent) {
|
|
69
|
-
return respond(404, null, {'statusText': S_NOT_FOUND});
|
|
97
|
+
return respond(fctx, 404, null, {'statusText': S_NOT_FOUND});
|
|
70
98
|
} else {
|
|
71
99
|
vs.delprop(ent, r'$KEY');
|
|
72
100
|
final out = vs.clone(ent);
|
|
73
|
-
return respond(200, out);
|
|
101
|
+
return respond(fctx, 200, out);
|
|
74
102
|
}
|
|
75
103
|
} else if ('list' == op.name) {
|
|
76
104
|
final args = self.buildArgs(fctx, op, fctx.reqmatch);
|
|
77
105
|
final found = vs.select(entmap, args);
|
|
78
106
|
if (null == found) {
|
|
79
|
-
return respond(404, null, {'statusText': S_NOT_FOUND});
|
|
107
|
+
return respond(fctx, 404, null, {'statusText': S_NOT_FOUND});
|
|
80
108
|
} else {
|
|
81
109
|
for (final ent in found) {
|
|
82
110
|
vs.delprop(ent, r'$KEY');
|
|
83
111
|
}
|
|
84
112
|
final out = vs.clone(found);
|
|
85
|
-
return respond(200, out);
|
|
113
|
+
return respond(fctx, 200, out);
|
|
86
114
|
}
|
|
87
115
|
} else if ('update' == op.name) {
|
|
88
116
|
final args = self.buildArgs(fctx, op, fctx.reqdata);
|
|
89
117
|
final found = vs.select(entmap, args);
|
|
90
118
|
final ent = vs.getelem(found, 0);
|
|
91
119
|
if (null == ent) {
|
|
92
|
-
return respond(404, null, {'statusText': S_NOT_FOUND});
|
|
120
|
+
return respond(fctx, 404, null, {'statusText': S_NOT_FOUND});
|
|
93
121
|
} else {
|
|
94
122
|
// Dart's single null stands in for the donor's undefined: merge
|
|
95
123
|
// must not overwrite stored values with absent ones.
|
|
@@ -104,7 +132,7 @@ class TestFeature extends BaseFeature {
|
|
|
104
132
|
vs.merge([ent, upddata]);
|
|
105
133
|
vs.delprop(ent, r'$KEY');
|
|
106
134
|
final out = vs.clone(ent);
|
|
107
|
-
return respond(200, out);
|
|
135
|
+
return respond(fctx, 200, out);
|
|
108
136
|
}
|
|
109
137
|
} else if ('remove' == op.name) {
|
|
110
138
|
final args = self.buildArgs(fctx, op, fctx.reqmatch);
|
|
@@ -115,7 +143,7 @@ class TestFeature extends BaseFeature {
|
|
|
115
143
|
if (null != ent) {
|
|
116
144
|
vs.delprop(entmap, vs.getprop(ent, 'id'));
|
|
117
145
|
}
|
|
118
|
-
return respond(200);
|
|
146
|
+
return respond(fctx, 200);
|
|
119
147
|
} else if ('create' == op.name) {
|
|
120
148
|
self.buildArgs(fctx, op, fctx.reqdata);
|
|
121
149
|
dynamic id = param(fctx, 'id');
|
|
@@ -130,7 +158,7 @@ class TestFeature extends BaseFeature {
|
|
|
130
158
|
vs.setprop(entmap, id, ent);
|
|
131
159
|
vs.delprop(ent, r'$KEY');
|
|
132
160
|
final out = vs.clone(ent);
|
|
133
|
-
return respond(200, out);
|
|
161
|
+
return respond(fctx, 200, out);
|
|
134
162
|
}
|
|
135
163
|
|
|
136
164
|
return null;
|
|
@@ -285,7 +285,7 @@ defmodule ProjectName.Feature.Test do
|
|
|
285
285
|
netsleep.(pick_latency(net))
|
|
286
286
|
status = H.or_(S.getprop(net, "failStatus"), 503)
|
|
287
287
|
|
|
288
|
-
respond(status, nil,
|
|
288
|
+
respond(fctx, status, nil,
|
|
289
289
|
S.jm(["statusText", "Simulated Failure", "headers", S.jm([])]))
|
|
290
290
|
|
|
291
291
|
true ->
|
|
@@ -205,6 +205,23 @@ def liveFetcher : SdkFeature.Fetcher := fun _ctx url fetchdef => do
|
|
|
205
205
|
("body", json), ("headers", ← emptyMap)]
|
|
206
206
|
pure (resp, none)
|
|
207
207
|
|
|
208
|
+
/-- THE MOCK HAS TO AGREE WITH THE MODEL. A point carrying
|
|
209
|
+
`transform.res: `body.item`` describes an API that answers {"item": {...}}
|
|
210
|
+
and the response transform unwraps that key on the way back. Returning the
|
|
211
|
+
bare payload means the transform unwraps a property that is not there and
|
|
212
|
+
the caller gets nothing. Mirrors the go/ts/lua/php mocks. -/
|
|
213
|
+
def mockEnvelope (ctx : Value) (data : Value) : SIO Value := do
|
|
214
|
+
if isNv data then pure data else do
|
|
215
|
+
let tm ← gp (← gp ctx "point") "transform"
|
|
216
|
+
let spec := asStr (← gp tm "res")
|
|
217
|
+
-- Exactly `body.<key>`; a deeper path is not an envelope this mock can
|
|
218
|
+
-- synthesise, so it is left alone rather than guessed at.
|
|
219
|
+
if spec.startsWith "`body." && spec.endsWith "`" && spec.length > 7 then
|
|
220
|
+
let inner := ((spec.drop 6).dropRight 1).toString
|
|
221
|
+
if inner.isEmpty || inner.contains '.' then pure data
|
|
222
|
+
else newMap #[(inner, data)]
|
|
223
|
+
else pure data
|
|
224
|
+
|
|
208
225
|
/-- The base transport in test mode: answer from the seeded store. -/
|
|
209
226
|
def testFetcher : SdkFeature.Fetcher := fun ctx _url _fetchdef => do
|
|
210
227
|
let client ← gp ctx "client"
|
|
@@ -213,8 +230,9 @@ def testFetcher : SdkFeature.Fetcher := fun ctx _url _fetchdef => do
|
|
|
213
230
|
let matchV ← gp ctx "reqmatch"
|
|
214
231
|
let dataV ← gp ctx "reqdata"
|
|
215
232
|
let out ← mockOp client entityName opName matchV dataV
|
|
233
|
+
let payload ← mockEnvelope ctx out
|
|
216
234
|
let resp ← newMap #[("status", .num 200.0), ("statusText", .str "OK"),
|
|
217
|
-
("body",
|
|
235
|
+
("body", payload), ("headers", ← emptyMap)]
|
|
218
236
|
pure (resp, none)
|
|
219
237
|
|
|
220
238
|
/-- Merge config.feature and options.feature into the client's feature options. -/
|
|
@@ -595,8 +595,6 @@ def makeResult (ctx : Value) : SIO Value := do
|
|
|
595
595
|
sp ctx "result" res
|
|
596
596
|
pure res
|
|
597
597
|
|
|
598
|
-
/-- Select the endpoint for this operation: the single point, else the first
|
|
599
|
-
whose `select.exist` keys are all present and whose `$action` agrees. -/
|
|
600
598
|
/-- How many path segments a point has, and whether its path ends in a
|
|
601
599
|
parameter. A record route ends in the record's identifier (/boards/{id});
|
|
602
600
|
a cross-reference that also returns the entity ends in the relationship's
|
|
@@ -611,6 +609,8 @@ def pointShape (pt : Value) : SIO (Nat × Bool) := do
|
|
|
611
609
|
else pure (items.size, (vs items[items.size - 1]!).startsWith "{")
|
|
612
610
|
| _ => pure (0, false)
|
|
613
611
|
|
|
612
|
+
/-- Select the endpoint for this operation: the single point, else the first
|
|
613
|
+
whose `select.exist` keys are all present and whose `$action` agrees. -/
|
|
614
614
|
def makePoint (ctx : Value) : SIO Value := do
|
|
615
615
|
let op ← gp ctx "op"
|
|
616
616
|
let matchV ← gp ctx "reqmatch"
|
|
@@ -980,8 +980,27 @@ let netsim_feature () : feature =
|
|
|
980
980
|
let test_feature () : feature =
|
|
981
981
|
let f = { f_name = "test"; f_version = "0.0.1"; f_active = true; f_options = Noval;
|
|
982
982
|
f_init = (fun _ _ -> ()); f_hook = (fun _ _ -> ()) } in
|
|
983
|
-
|
|
984
|
-
|
|
983
|
+
(* THE MOCK HAS TO AGREE WITH THE MODEL. A point carrying
|
|
984
|
+
`transform.res: `body.item`` describes an API that answers {"item": {...}}
|
|
985
|
+
and the response transform unwraps that key on the way back. Returning the
|
|
986
|
+
bare payload means the transform unwraps a property that is not there and
|
|
987
|
+
the caller gets nothing. Mirrors the go/ts/lua/php mocks. *)
|
|
988
|
+
let envelope ctx data =
|
|
989
|
+
if is_nullish data then data
|
|
990
|
+
else
|
|
991
|
+
match getp (getp ctx.c_point "transform") "res" with
|
|
992
|
+
| Str spec ->
|
|
993
|
+
let n = String.length spec in
|
|
994
|
+
(* Exactly `body.<key>`; a deeper path is not an envelope this mock
|
|
995
|
+
can synthesise, so it is left alone rather than guessed at. *)
|
|
996
|
+
if n > 7 && String.sub spec 0 6 = "`body." && spec.[n - 1] = '`' then
|
|
997
|
+
let inner = String.sub spec 6 (n - 7) in
|
|
998
|
+
if String.length inner = 0 || String.contains inner '.' then data
|
|
999
|
+
else jo [(inner, data)]
|
|
1000
|
+
else data
|
|
1001
|
+
| _ -> data in
|
|
1002
|
+
let respond ctx status data extra =
|
|
1003
|
+
let out = jo [("status", vint_of status); ("statusText", Str "OK"); ("json", json_thunk (envelope ctx data)); ("body", Str "not-used")] in
|
|
985
1004
|
(match extra with Some (Map _ as e) -> List.iter (fun k -> setp out k (getp e k)) (keysof e) | _ -> ());
|
|
986
1005
|
(out, None) in
|
|
987
1006
|
let build_args ctx (op : operation) args =
|
|
@@ -1027,15 +1046,15 @@ let test_feature () : feature =
|
|
|
1027
1046
|
| "load" ->
|
|
1028
1047
|
let args = build_args fctx op (resolve_match fctx fctx.c_reqmatch) in
|
|
1029
1048
|
let ent = getelem (select entmap args) (Num 0.0) in
|
|
1030
|
-
if is_nullish ent then respond 404 Noval (Some (jo [("statusText", Str "Not found")]))
|
|
1031
|
-
else (ignore (delprop ent (Str "$KEY")); respond 200 (clone ent) None)
|
|
1049
|
+
if is_nullish ent then respond fctx 404 Noval (Some (jo [("statusText", Str "Not found")]))
|
|
1050
|
+
else (ignore (delprop ent (Str "$KEY")); respond fctx 200 (clone ent) None)
|
|
1032
1051
|
| "list" ->
|
|
1033
1052
|
let args = build_args fctx op fctx.c_reqmatch in
|
|
1034
1053
|
let found = select entmap args in
|
|
1035
|
-
if is_nullish found then respond 404 Noval (Some (jo [("statusText", Str "Not found")]))
|
|
1054
|
+
if is_nullish found then respond fctx 404 Noval (Some (jo [("statusText", Str "Not found")]))
|
|
1036
1055
|
else begin
|
|
1037
1056
|
(match found with List r -> List.iter (fun item -> ignore (delprop item (Str "$KEY"))) !r | _ -> ());
|
|
1038
|
-
respond 200 (clone found) None
|
|
1057
|
+
respond fctx 200 (clone found) None
|
|
1039
1058
|
end
|
|
1040
1059
|
| "update" ->
|
|
1041
1060
|
let update_match = empty_map () in
|
|
@@ -1046,17 +1065,17 @@ let test_feature () : feature =
|
|
|
1046
1065
|
(if is_nullish !ent then match entmap with
|
|
1047
1066
|
| Map m -> (try (match List.find (fun (_, v) -> match v with Map _ -> true | _ -> false) m.entries with (_, v) -> ent := v) with Not_found -> ())
|
|
1048
1067
|
| _ -> ());
|
|
1049
|
-
if is_nullish !ent then respond 404 Noval (Some (jo [("statusText", Str "Not found")]))
|
|
1068
|
+
if is_nullish !ent then respond fctx 404 Noval (Some (jo [("statusText", Str "Not found")]))
|
|
1050
1069
|
else begin
|
|
1051
1070
|
(match !ent with Map _ -> (match fctx.c_reqdata with Map _ -> List.iter (fun k -> setp !ent k (getp fctx.c_reqdata k)) (keysof fctx.c_reqdata) | _ -> ()) | _ -> ());
|
|
1052
1071
|
ignore (delprop !ent (Str "$KEY"));
|
|
1053
|
-
respond 200 (clone !ent) None
|
|
1072
|
+
respond fctx 200 (clone !ent) None
|
|
1054
1073
|
end
|
|
1055
1074
|
| "remove" ->
|
|
1056
1075
|
let args = build_args fctx op (resolve_match fctx fctx.c_reqmatch) in
|
|
1057
1076
|
let ent = getelem (select entmap args) (Num 0.0) in
|
|
1058
1077
|
(match ent with Map _ -> ignore (delprop entmap (getp ent "id")) | _ -> ());
|
|
1059
|
-
respond 200 Noval None
|
|
1078
|
+
respond fctx 200 Noval None
|
|
1060
1079
|
| "create" ->
|
|
1061
1080
|
ignore (build_args fctx op fctx.c_reqdata);
|
|
1062
1081
|
let eid = let v = (cu fctx).u_param fctx (Str "id") in if is_nullish v then Str (random_id16 ()) else v in
|
|
@@ -1066,9 +1085,9 @@ let test_feature () : feature =
|
|
|
1066
1085
|
setp ent "id" eid;
|
|
1067
1086
|
(match eid with Str s -> setp entmap s ent | _ -> ());
|
|
1068
1087
|
ignore (delprop ent (Str "$KEY"));
|
|
1069
|
-
respond 200 (clone ent) None
|
|
1070
|
-
| _ -> respond 200 ent None)
|
|
1071
|
-
| _ -> respond 404 Noval (Some (jo [("statusText", Str "Unknown operation")]))) in
|
|
1088
|
+
respond fctx 200 (clone ent) None
|
|
1089
|
+
| _ -> respond fctx 200 ent None)
|
|
1090
|
+
| _ -> respond fctx 404 Noval (Some (jo [("statusText", Str "Unknown operation")]))) in
|
|
1072
1091
|
let make_netsim net inner =
|
|
1073
1092
|
let netcalls = ref 0 in
|
|
1074
1093
|
let pick_latency () =
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Make the suite runnable with a bare `make test`, the way every other
|
|
2
|
+
# target's is.
|
|
3
|
+
#
|
|
4
|
+
# py-data is the one target that CONSUMES a sibling: its package imports the
|
|
5
|
+
# ProjectName SDK generated into ../py of the same repo. The Makefile's `dev`
|
|
6
|
+
# target pip-installs both editable, but `test` does not depend on it, so
|
|
7
|
+
# `make test` on a fresh clone died in collection with
|
|
8
|
+
# "ModuleNotFoundError: No module named 'projectname_sdk'" — before a single
|
|
9
|
+
# assertion ran.
|
|
10
|
+
#
|
|
11
|
+
# Requiring an install step would also make this target the only one that
|
|
12
|
+
# cannot be tested from a clean checkout. Putting the two source roots on
|
|
13
|
+
# sys.path keeps it self-contained; an installed copy still wins, because
|
|
14
|
+
# these entries are appended only when the import is not already satisfiable.
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import sys
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
_here = Path(__file__).resolve().parent
|
|
21
|
+
for _root in (_here.parent, _here.parent.parent / "py"):
|
|
22
|
+
_p = str(_root)
|
|
23
|
+
if _root.is_dir() and _p not in sys.path:
|
|
24
|
+
sys.path.insert(0, _p)
|
|
@@ -172,22 +172,22 @@ public final class TestFeature: BaseFeature {
|
|
|
172
172
|
if isNil(ent) {
|
|
173
173
|
let extra = VMap()
|
|
174
174
|
extra.entries["statusText"] = .string("Not found")
|
|
175
|
-
return testRespond(
|
|
175
|
+
return testRespond(ctx2, 404, .noval, extra)
|
|
176
176
|
}
|
|
177
177
|
delprop(ent, .string("$KEY"))
|
|
178
|
-
return testRespond(
|
|
178
|
+
return testRespond(ctx2, 200, clone(ent), nil)
|
|
179
179
|
} else if op.name == "list" {
|
|
180
180
|
let args = testBuildArgs(ctx2, op, ctx2.reqmatch)
|
|
181
181
|
let found = select(.map(entmap), args)
|
|
182
182
|
if isNil(found) {
|
|
183
183
|
let extra = VMap()
|
|
184
184
|
extra.entries["statusText"] = .string("Not found")
|
|
185
|
-
return testRespond(
|
|
185
|
+
return testRespond(ctx2, 404, .noval, extra)
|
|
186
186
|
}
|
|
187
187
|
if let fl = found.asList {
|
|
188
188
|
for item in fl.items { delprop(item, .string("$KEY")) }
|
|
189
189
|
}
|
|
190
|
-
return testRespond(
|
|
190
|
+
return testRespond(ctx2, 200, clone(found), nil)
|
|
191
191
|
} else if op.name == "update" {
|
|
192
192
|
var updateMatch = VMap()
|
|
193
193
|
if let idv = ctx2.reqdata.entries["id"] {
|
|
@@ -209,13 +209,13 @@ public final class TestFeature: BaseFeature {
|
|
|
209
209
|
if isNil(ent) {
|
|
210
210
|
let extra = VMap()
|
|
211
211
|
extra.entries["statusText"] = .string("Not found")
|
|
212
|
-
return testRespond(
|
|
212
|
+
return testRespond(ctx2, 404, .noval, extra)
|
|
213
213
|
}
|
|
214
214
|
if let entm = ent.asMap {
|
|
215
215
|
for (k, v) in ctx2.reqdata.entries { entm.entries[k] = v }
|
|
216
216
|
}
|
|
217
217
|
delprop(ent, .string("$KEY"))
|
|
218
|
-
return testRespond(
|
|
218
|
+
return testRespond(ctx2, 200, clone(ent), nil)
|
|
219
219
|
} else if op.name == "remove" {
|
|
220
220
|
let args = testBuildArgs(ctx2, op, testResolveMatch(ctx2, ctx2.reqmatch))
|
|
221
221
|
let found = select(.map(entmap), args)
|
|
@@ -224,7 +224,7 @@ public final class TestFeature: BaseFeature {
|
|
|
224
224
|
let id = gp(entm2, "id")
|
|
225
225
|
delprop(.map(entmap), id)
|
|
226
226
|
}
|
|
227
|
-
return testRespond(
|
|
227
|
+
return testRespond(ctx2, 200, .noval, nil)
|
|
228
228
|
} else if op.name == "create" {
|
|
229
229
|
_ = testBuildArgs(ctx2, op, ctx2.reqdata)
|
|
230
230
|
var id = ctx2.utility!.param(ctx2, .string("id"))
|
|
@@ -239,14 +239,14 @@ public final class TestFeature: BaseFeature {
|
|
|
239
239
|
entm.entries["id"] = id
|
|
240
240
|
if let idStr = id.asString { entmap.entries[idStr] = .map(entm) }
|
|
241
241
|
delprop(.map(entm), .string("$KEY"))
|
|
242
|
-
return testRespond(
|
|
242
|
+
return testRespond(ctx2, 200, clone(.map(entm)), nil)
|
|
243
243
|
}
|
|
244
|
-
return testRespond(
|
|
244
|
+
return testRespond(ctx2, 200, ent, nil)
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
let extra = VMap()
|
|
248
248
|
extra.entries["statusText"] = .string("Unknown operation")
|
|
249
|
-
return testRespond(
|
|
249
|
+
return testRespond(ctx2, 404, .noval, extra)
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
// Optional network behaviour simulation over the mock transport.
|
package/src/helpers/canonType.ts
CHANGED
|
@@ -182,6 +182,43 @@ function canonKey(sentinel: unknown): string {
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
|
|
185
|
+
// The bare key of the sentinel a VALUE of this type must satisfy, resolving
|
|
186
|
+
// the union sentinel `['`$ONE`', [member, ...]]` to its first non-NULL member
|
|
187
|
+
// (recursively — a member may itself be a union).
|
|
188
|
+
//
|
|
189
|
+
// canonKey alone cannot do this: given an array it stringifies it, so a
|
|
190
|
+
// nullable `['`$ONE`', ['`$NUMBER`','`$NULL`']]` field yields neither NUMBER
|
|
191
|
+
// nor anything else recognizable and every caller falls through to its
|
|
192
|
+
// "unknown, use a string" branch. canonToType meanwhile renders that same
|
|
193
|
+
// sentinel as `number | null`, so the doc example said `'..._id'` while the
|
|
194
|
+
// generated type said `number` — a TS2322 in the SDK's own README, which is
|
|
195
|
+
// exactly the disagreement the example helpers exist to prevent.
|
|
196
|
+
//
|
|
197
|
+
// A union of nothing but NULL resolves to NULL; an unrecognized array shape,
|
|
198
|
+
// or an empty member list, resolves to '' (unknown) like any missing
|
|
199
|
+
// sentinel.
|
|
200
|
+
function canonScalarKey(sentinel: unknown): string {
|
|
201
|
+
if (Array.isArray(sentinel)) {
|
|
202
|
+
if ('ONE' !== canonKey(sentinel[0]) || !Array.isArray(sentinel[1])) {
|
|
203
|
+
return ''
|
|
204
|
+
}
|
|
205
|
+
let sawNull = false
|
|
206
|
+
for (const member of sentinel[1]) {
|
|
207
|
+
const key = canonScalarKey(member)
|
|
208
|
+
if ('NULL' === key) {
|
|
209
|
+
sawNull = true
|
|
210
|
+
continue
|
|
211
|
+
}
|
|
212
|
+
if ('' !== key) {
|
|
213
|
+
return key
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return sawNull ? 'NULL' : ''
|
|
217
|
+
}
|
|
218
|
+
return canonKey(sentinel)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
185
222
|
// Map a field/param type sentinel to a target-language primitive type.
|
|
186
223
|
// Unknown or missing sentinel -> that language's "any" (never throws).
|
|
187
224
|
//
|
|
@@ -220,6 +257,7 @@ export {
|
|
|
220
257
|
canonToType,
|
|
221
258
|
canonToDtype,
|
|
222
259
|
canonKey,
|
|
260
|
+
canonScalarKey,
|
|
223
261
|
CANON_TYPE,
|
|
224
262
|
CANON_ANY,
|
|
225
263
|
CANON_UNION_JOIN,
|
package/src/sdkgen.ts
CHANGED
|
@@ -57,7 +57,7 @@ import { buildIdNames } from './helpers/buildIdNames'
|
|
|
57
57
|
import { getMatchEntries } from './helpers/getMatchEntries'
|
|
58
58
|
import { collectDeps } from './helpers/collectDeps'
|
|
59
59
|
import type { DepEntry } from './helpers/collectDeps'
|
|
60
|
-
import { canonToType, canonToDtype, canonKey } from './helpers/canonType'
|
|
60
|
+
import { canonToType, canonToDtype, canonKey, canonScalarKey } from './helpers/canonType'
|
|
61
61
|
import { OP_SUFFIX, opTypeName, opParams, ownPoint, opActions, entityActions, entityPath, opRequestShape, entityIdField, entityDataIdField, entityOps, entityPrimaryOp, pickExampleEntity, entityClassName, entityTypeCollisions, warnEntityTypeCollisions, deriveEntityNames, entityCollection } from './helpers/opShape'
|
|
62
62
|
import { isReservedName, safeVarName, exampleVarName, phpEntityAccessor, entityCacheField, isRbCoreConstant, isRbSdkConstant, rbSafeTypeName, isSwiftSdkType, swiftSafeTypeName, isPhpReservedType, phpSafeTypeName, isTsReservedType, tsSafeTypeName, jsProp, jsOptProp, jsKey } from './helpers/naming'
|
|
63
63
|
import { serverVariables, hasServerVariables } from './helpers/serverVars'
|
|
@@ -1059,6 +1059,7 @@ export {
|
|
|
1059
1059
|
canonToType,
|
|
1060
1060
|
canonToDtype,
|
|
1061
1061
|
canonKey,
|
|
1062
|
+
canonScalarKey,
|
|
1062
1063
|
|
|
1063
1064
|
OP_SUFFIX,
|
|
1064
1065
|
opTypeName,
|